TenantAtlas/specs/224-findings-notifications-escalation/contracts/findings-notifications-escalation.logical.openapi.yaml
2026-04-22 02:51:50 +02:00

349 lines
10 KiB
YAML

openapi: 3.1.0
info:
title: Findings Notifications & Escalation Surface Contract
version: 1.0.0
summary: Logical internal contract for Spec 224 delivery, alert-rule exposure, and finding deep links.
description: |
This contract documents the structured payloads and UI-facing surfaces that Spec 224 must satisfy.
It is intentionally logical rather than public-API only: the feature reuses existing Filament resources,
database notifications, and background jobs instead of introducing a new public controller namespace.
servers:
- url: https://logical.internal
description: Non-routable placeholder used to describe internal repository contracts.
paths:
/internal/findings/notification-events:
post:
summary: Dispatch one finding event to direct personal delivery and optional external alert copies.
description: |
Logical internal contract implemented by the bounded finding-notification delivery seam.
It normalizes one finding event, resolves at most one entitled direct recipient, writes one
Filament-compatible database notification when appropriate, and forwards the same event to the
existing workspace alert dispatch pipeline.
operationId: dispatchFindingNotificationEvent
x-not-public-http: true
requestBody:
required: true
content:
application/vnd.tenantpilot.finding-notification-event+json:
schema:
$ref: '#/components/schemas/FindingNotificationEventDispatch'
responses:
'202':
description: Event accepted and evaluated for direct and optional external delivery.
content:
application/vnd.tenantpilot.finding-notification-dispatch-result+json:
schema:
$ref: '#/components/schemas/FindingNotificationDispatchResult'
/admin/alert-rules:
get:
summary: Existing alert-rule surfaces expose the four new finding event types.
description: |
Existing Filament resource pages continue to render HTML, while this logical media type documents
the event-type options that must appear in create, edit, and filter flows.
operationId: viewFindingAlertRuleOptions
responses:
'200':
description: Alert-rule surfaces show the finding event options and reuse the existing delivery families.
content:
text/html:
schema:
type: string
application/vnd.tenantpilot.finding-alert-rule-options+json:
schema:
$ref: '#/components/schemas/FindingAlertRuleOptionsSurface'
'403':
description: Workspace operator lacks permission to view alert-rule configuration.
'404':
description: Workspace operator is not a member of the active workspace or the alert surface is outside their visible scope.
/admin/alert-deliveries:
get:
summary: Existing alert-delivery history can filter and label finding event copies.
operationId: viewFindingAlertDeliveries
parameters:
- name: event_type
in: query
required: false
schema:
$ref: '#/components/schemas/FindingEventType'
responses:
'200':
description: Alert-delivery viewer surfaces show finding-event labels and safe summaries.
content:
text/html:
schema:
type: string
application/vnd.tenantpilot.finding-alert-deliveries+json:
schema:
$ref: '#/components/schemas/FindingAlertDeliveriesSurface'
'403':
description: Workspace operator lacks permission to inspect alert deliveries.
'404':
description: Workspace operator is not a member of the active workspace or the alert-delivery surface is outside their visible scope.
/admin/t/{tenant}/findings/{finding}:
get:
summary: The direct notification action opens the existing tenant finding detail route.
operationId: openFindingFromNotification
parameters:
- name: tenant
in: path
required: true
schema:
type: integer
- name: finding
in: path
required: true
schema:
type: integer
responses:
'200':
description: Existing finding detail renders for an entitled tenant operator.
content:
text/html:
schema:
type: string
application/vnd.tenantpilot.finding-notification-context+json:
schema:
$ref: '#/components/schemas/FindingNotificationContext'
'403':
description: Recipient still has tenant visibility but lacks current capability to inspect the finding.
'404':
description: Recipient no longer has tenant or record visibility.
components:
schemas:
FindingEventType:
type: string
enum:
- findings.assigned
- findings.reopened
- findings.due_soon
- findings.overdue
FindingSeverity:
type: string
enum:
- low
- medium
- high
- critical
RecipientReason:
type: string
enum:
- new_assignee
- current_assignee
- current_owner
DirectRecipient:
type: object
required:
- userId
- reason
properties:
userId:
type: integer
displayName:
type: string
reason:
$ref: '#/components/schemas/RecipientReason'
FindingNotificationEventDispatch:
type: object
required:
- eventType
- workspaceId
- tenantId
- findingId
- severity
- title
- body
- fingerprintKey
- metadata
properties:
eventType:
$ref: '#/components/schemas/FindingEventType'
workspaceId:
type: integer
tenantId:
type: integer
findingId:
type: integer
severity:
$ref: '#/components/schemas/FindingSeverity'
title:
type: string
body:
type: string
directRecipient:
oneOf:
- $ref: '#/components/schemas/DirectRecipient'
- type: 'null'
fingerprintKey:
type: string
dueCycleKey:
type:
- string
- 'null'
metadata:
type: object
required:
- tenantName
- recipientReason
properties:
tenantName:
type: string
recipientReason:
$ref: '#/components/schemas/RecipientReason'
ownerUserId:
type:
- integer
- 'null'
assigneeUserId:
type:
- integer
- 'null'
dueAt:
type:
- string
- 'null'
format: date-time
reopenedAt:
type:
- string
- 'null'
format: date-time
summary:
type: string
allOf:
- if:
properties:
eventType:
enum:
- findings.due_soon
- findings.overdue
then:
required:
- dueCycleKey
properties:
dueCycleKey:
type: string
- if:
properties:
eventType:
enum:
- findings.assigned
- findings.reopened
then:
properties:
dueCycleKey:
type: 'null'
FindingNotificationDispatchResult:
type: object
required:
- eventType
- fingerprintKey
- directDeliveryStatus
- externalDeliveryCount
properties:
eventType:
$ref: '#/components/schemas/FindingEventType'
fingerprintKey:
type: string
directDeliveryStatus:
type: string
enum:
- sent
- suppressed
- deduped
- no_recipient
externalDeliveryCount:
type: integer
minimum: 0
externalDeliveryStatuses:
type: array
items:
type: string
enum:
- queued
- deferred
- suppressed
- none
EventTypeOption:
type: object
required:
- value
- label
properties:
value:
$ref: '#/components/schemas/FindingEventType'
label:
type: string
FindingAlertRuleOptionsSurface:
type: object
required:
- eventTypes
- existingDeliveryFamiliesReused
properties:
eventTypes:
type: array
items:
$ref: '#/components/schemas/EventTypeOption'
existingDeliveryFamiliesReused:
type: boolean
notes:
type: array
items:
type: string
FindingAlertDeliveryRow:
type: object
required:
- deliveryId
- eventType
- label
- status
properties:
deliveryId:
type: integer
eventType:
$ref: '#/components/schemas/FindingEventType'
label:
type: string
tenantId:
type:
- integer
- 'null'
status:
type: string
destinationName:
type: string
summary:
type: string
FindingAlertDeliveriesSurface:
type: object
required:
- filterEventTypes
- rows
properties:
filterEventTypes:
type: array
items:
$ref: '#/components/schemas/EventTypeOption'
rows:
type: array
items:
$ref: '#/components/schemas/FindingAlertDeliveryRow'
FindingNotificationContext:
type: object
required:
- findingId
- tenantId
- eventType
- recipientReason
properties:
findingId:
type: integer
tenantId:
type: integer
eventType:
$ref: '#/components/schemas/FindingEventType'
recipientReason:
$ref: '#/components/schemas/RecipientReason'
title:
type: string
body:
type: string