{
  "openapi": "3.1.0",
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "info": {
    "title": "Relay integration design contract",
    "version": "0.1.0-design",
    "summary": "Proposed service-to-service case events, aggregate operations reporting and signed alerts.",
    "description": "DESIGN CONTRACT ONLY. No live API, deployed server, connected system or delivery capability is represented. No server URL is supplied. Read and write credentials are separate least-privilege service roles: events:write may ingest authorized verified case events; reports:read may read aggregate summaries. Credentials stay in server-side secret storage, never browser code. Tenant comes only from the authenticated credential; tenant fields/query parameters are forbidden. Before a 202 receipt, validate permissions, source authority, tenant references, idempotency, schema, evidence and known transition constraints, and retain an immutable event/audit record. Projection is asynchronous; accepted does not mean projected, resolved, sent or financially executed. Late/out-of-order events remain visible as pending or conflicted at a revision/watermark and must be reconciled without silent state changes. Apply data minimization, retention, access review and audit controls in implementation. No financial actions are exposed. Endpoint registration, credentials, webhooks, SLA calendars and replay controls still require implementation and acceptance."
  },
  "x-design-only": true,
  "x-no-live-service": true,
  "paths": {
    "/v1/case-events": {
      "post": {
        "operationId": "ingestCaseEvent",
        "summary": "Accept an authorized external case event for asynchronous projection",
        "description": "Proposed write role: events:write. Requires a service credential authorized for the source and all tenant-local references. This endpoint ingests verified facts and never executes a financial or messaging action. Reject known invalid transitions with 422. Valid late/out-of-order events with unresolved dependencies may be accepted pending reconciliation; any later projection conflict is surfaced in summary projectionIssues and retained in immutable audit. No raw message body or personal/account data is permitted. Idempotency is bound to authenticated tenant + operation + key, not to an untrusted source field. Same key and canonical payload return the original 202 receipt without a second event; different payload returns 409. EventId is also deduplicated tenant-wide; the exact retained receipt/retry retention policy must be agreed before implementation. A missing receipt is not proof of rejection or permission to change the event ID.",
        "x-required-scopes": [
          "events:write"
        ],
        "security": [
          {
            "ServiceBearer": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Client-generated opaque request key, stable for every retry of the identical event. No PII. Same key with different canonical payload is 409.",
            "schema": {
              "type": "string",
              "minLength": 16,
              "maxLength": 128,
              "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{15,127}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CaseEvent"
              },
              "example": {
                "eventId": "331a47b1-40e5-4a99-a369-3141fe435882",
                "caseRef": "CASE-DEMO-1042",
                "customerRef": "CUSTOMER-DEMO-18",
                "source": "demo-case-system",
                "occurredAt": "2026-10-23T11:30:00Z",
                "eventType": "case.opened",
                "originatingChannel": "web",
                "data": {
                  "initialStatus": "open"
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted into the proposed immutable event/audit intake. Not yet projected or resolved; no external action has executed. Duplicate same-payload retries return the original receipt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventAccepted"
                },
                "example": {
                  "eventId": "331a47b1-40e5-4a99-a369-3141fe435882",
                  "receiptRef": "RECEIPT-DEMO-1042",
                  "acceptedAt": "2026-10-23T11:30:01Z",
                  "status": "accepted",
                  "projectionStatus": "pending"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing/invalid Idempotency-Key or invalid query syntax.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or invalid service credential.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Credential lacks the operation scope, or a reference is outside the authenticated tenant. No cross-tenant record details are disclosed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "An Idempotency-Key or eventId has already been used with a different canonical payload. No replacement or action is performed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Schema, evidence, source authority or known case-transition validation failed. No event is accepted.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Respect Retry-After; retry the same event with the same Idempotency-Key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds before another request should be attempted.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "503": {
            "description": "Proposed service unavailable. Outcome must be checked through the agreed reconciliation procedure; never infer an external financial outcome.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/v1/operations/summary": {
      "get": {
        "operationId": "getOperationsSummary",
        "summary": "Read a consistent aggregate operations snapshot",
        "description": "Proposed read role: reports:read. Tenant isolation is enforced through the authenticated service credential, not a tenant query. Only aggregate counts are returned; individual case, customer, owner, account and message records are excluded. All filters apply before every metric and optional breakdown. Counts are case counts, not message-delivery statistics. Permission to read reports does not permit event ingestion. The proposed first-human-reply numerator/denominator requires an authoritative reply/SLA projection source; case.message_received does not itself establish a reply. This bounded event contract does not claim a complete metrics-ingestion pipeline.",
        "x-required-scopes": [
          "reports:read"
        ],
        "security": [
          {
            "ServiceBearer": []
          }
        ],
        "parameters": [
          {
            "name": "window",
            "in": "query",
            "required": false,
            "description": "Creation cohort window; defaults to the dashboard 7-day view.",
            "schema": {
              "type": "string",
              "enum": [
                "7d",
                "30d"
              ],
              "default": "7d"
            }
          },
          {
            "name": "department",
            "in": "query",
            "required": false,
            "description": "Accepted accountable department at the snapshot; omitted means all.",
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "merchant",
                "integrations",
                "settlement",
                "risk",
                "unassigned"
              ],
              "default": "all"
            }
          },
          {
            "name": "channel",
            "in": "query",
            "required": false,
            "description": "Single originating channel, independent of later channel handoffs. Omitted means all.",
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "whatsapp",
                "telegram",
                "web",
                "app"
              ],
              "default": "all"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Aggregate snapshot at a stated projection revision and ingestion watermark. Example is synthetic and reconciles to the Relay dashboard default view.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OperationsSummary"
                },
                "example": {
                  "snapshotAt": "2026-10-23T12:00:00Z",
                  "timezone": "Asia/Dubai",
                  "window": "7d",
                  "department": "all",
                  "channel": "all",
                  "cohortBasis": "case_created_at",
                  "channelAttribution": "originating_channel",
                  "projectionThrough": "2026-10-23T11:59:00Z",
                  "revision": "demo-revision-024",
                  "counts": {
                    "cases": 18,
                    "open": 8,
                    "resolutionOverdue": 3,
                    "firstReplyWithinTarget": 13,
                    "firstReplyEligible": 17
                  },
                  "projectionIssues": {
                    "pendingDependencies": 0,
                    "invalidTransitions": 0
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing/invalid Idempotency-Key or invalid query syntax.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or invalid service credential.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Credential lacks the operation scope, or a reference is outside the authenticated tenant. No cross-tenant record details are disclosed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Schema, evidence, source authority or known case-transition validation failed. No event is accepted.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Respect Retry-After; retry the same event with the same Idempotency-Key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds before another request should be attempted.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "503": {
            "description": "Proposed service unavailable. Outcome must be checked through the agreed reconciliation procedure; never infer an external financial outcome.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "support.alert": {
      "post": {
        "operationId": "receiveSupportAlert",
        "summary": "Proposed signed Relay alert delivered to an agreed receiving service",
        "description": "DESIGN ONLY: no callback URL, registration endpoint, subscription, server or actual delivery is supplied. Alerts require an explicitly authorized server-to-server recipient bound to one tenant in service configuration. Verify signature and timestamp before parsing/trusting the payload. Proposed signature: X-Relay-Signature is sha256= plus lowercase HMAC-SHA256 hex over UTF-8(timestamp + \".\" + exact raw request body); use a separately provisioned recipient secret, constant-time comparison and rotation. X-Relay-Timestamp is integer Unix seconds. Reject a delivery whose timestamp differs from receiver time by more than 300 seconds; record eventId/delivery identity to prevent replay, and deduplicate the logical event across redeliveries. Legitimate retries retain eventId but receive a fresh timestamp/signature. Out-of-order notifications are reconciled by revision and authoritative state, not blindly applied. No browser credential or signature secret is exposed. Bearer service credentials for the read/write API do not authenticate webhook deliveries. Exact retry schedule, retention and receiver URL are implementation decisions.",
        "security": [
          {
            "WebhookSignature": [],
            "WebhookTimestamp": []
          }
        ],
        "x-replay-window-seconds": 300,
        "parameters": [
          {
            "name": "X-Relay-Signature",
            "in": "header",
            "required": true,
            "description": "Proposed HMAC-SHA256 signature over timestamp, dot and exact raw request body, using a separately configured recipient secret.",
            "schema": {
              "type": "string",
              "pattern": "^sha256=[a-f0-9]{64}$"
            }
          },
          {
            "name": "X-Relay-Timestamp",
            "in": "header",
            "required": true,
            "description": "Signed Unix seconds; enforce the 300-second replay window and event deduplication.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9]{10,}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SupportAlert"
              },
              "example": {
                "eventId": "551928d4-7f27-47f1-9fbd-6606550ca6df",
                "caseRef": "CASE-DEMO-1042",
                "eventType": "case.sla_at_risk",
                "occurredAt": "2026-10-23T11:45:00Z",
                "revision": "demo-revision-024"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Receiving service verified signature/replay controls and durably acknowledged this notification; acknowledgement does not resolve the case or authorize a financial action."
          },
          "400": {
            "description": "Malformed JSON, missing/invalid Idempotency-Key or invalid query syntax.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid signature, stale timestamp or rejected replay.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Credential lacks the operation scope, or a reference is outside the authenticated tenant. No cross-tenant record details are disclosed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Schema, evidence, source authority or known case-transition validation failed. No event is accepted.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Respect Retry-After; retry the same event with the same Idempotency-Key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds before another request should be attempted.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "503": {
            "description": "Proposed service unavailable. Outcome must be checked through the agreed reconciliation procedure; never infer an external financial outcome.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ServiceBearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Proposed opaque server-side service credential, bound to one tenant and allowed sources. Separate role grants events:write and/or reports:read are enforced per operation; these are application permissions, not OAuth scope claims. Never embed this key in a browser."
      },
      "WebhookSignature": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Relay-Signature",
        "description": "Proposed HMAC-SHA256 signature; recipient secret is provisioned outside this contract."
      },
      "WebhookTimestamp": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Relay-Timestamp",
        "description": "Timestamp authenticated by the signature; reject outside the 300-second replay window."
      }
    },
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "Problem Details error. No customer/account details, secrets or evidence payloads are returned. This design uses stable codes; HTTP status and problem status agree.",
        "additionalProperties": false,
        "required": [
          "type",
          "title",
          "status",
          "code",
          "requestId"
        ],
        "properties": {
          "type": {
            "type": "string",
            "description": "Stable problem type identifier; about:blank when no dedicated type is defined.",
            "default": "about:blank"
          },
          "title": {
            "type": "string",
            "description": "Safe summary of the error.",
            "maxLength": 160
          },
          "status": {
            "type": "integer",
            "minimum": 400,
            "maximum": 599
          },
          "code": {
            "type": "string",
            "enum": [
              "invalid_request",
              "unauthenticated",
              "forbidden",
              "cross_tenant_reference",
              "idempotency_conflict",
              "event_conflict",
              "invalid_event",
              "invalid_transition",
              "invalid_query",
              "rate_limited",
              "unavailable"
            ]
          },
          "requestId": {
            "type": "string",
            "description": "Opaque request correlation reference. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
          },
          "detail": {
            "type": "string",
            "description": "Sanitized diagnostic text; no raw event or customer data.",
            "maxLength": 500
          }
        }
      },
      "Counts": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "cases",
          "open",
          "resolutionOverdue",
          "firstReplyWithinTarget",
          "firstReplyEligible"
        ],
        "description": "All fields use the identical filtered case-created cohort, snapshot, SLA policy and projection revision. Invariants: resolutionOverdue <= open <= cases; firstReplyWithinTarget <= firstReplyEligible <= cases. First-reply target results are service metrics, not financial outcomes.",
        "properties": {
          "cases": {
            "type": "integer",
            "minimum": 0,
            "description": "Total distinct cases created in the selected window and matching all filters."
          },
          "open": {
            "type": "integer",
            "minimum": 0,
            "description": "Cases in the same cohort without a verified resolution at snapshotAt. Owner or channel changes do not create a new case."
          },
          "resolutionOverdue": {
            "type": "integer",
            "minimum": 0,
            "description": "Open cohort cases whose resolution SLA deadline is strictly before snapshotAt; a subset of open."
          },
          "firstReplyWithinTarget": {
            "type": "integer",
            "minimum": 0,
            "description": "Eligible cohort cases with a verified first human reply at or before their first-reply target deadline; a subset of firstReplyEligible."
          },
          "firstReplyEligible": {
            "type": "integer",
            "minimum": 0,
            "description": "Cohort cases with an observed first human reply, or whose first-reply deadline is strictly before snapshotAt. Unreplied cases still within their target, including exactly at the deadline, are excluded. The denominator is not all cases."
          }
        }
      },
      "EventAccepted": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "eventId",
          "receiptRef",
          "acceptedAt",
          "status",
          "projectionStatus"
        ],
        "properties": {
          "eventId": {
            "type": "string",
            "format": "uuid"
          },
          "receiptRef": {
            "type": "string",
            "description": "Immutable audit receipt reference. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
          },
          "acceptedAt": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "const": "accepted"
          },
          "projectionStatus": {
            "type": "string",
            "const": "pending"
          }
        },
        "description": "202 confirms permission, source authority, schema and acceptance validation followed by durable immutable event/audit recording in the proposed design. It does not confirm projection, case resolution, communication delivery, account posting or any financial action. Late/out-of-order events remain pending until dependencies are reconciled."
      },
      "OperationsSummary": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "snapshotAt",
          "timezone",
          "window",
          "department",
          "channel",
          "cohortBasis",
          "channelAttribution",
          "projectionThrough",
          "revision",
          "counts",
          "projectionIssues"
        ],
        "properties": {
          "snapshotAt": {
            "type": "string",
            "format": "date-time",
            "description": "As-of timestamp used for SLA and state calculations; not response wall-clock time."
          },
          "timezone": {
            "type": "string",
            "const": "Asia/Dubai",
            "description": "Timezone used for the proposed dashboard cohort boundaries; timezone and SLA calendar are agreed per implementation."
          },
          "window": {
            "type": "string",
            "enum": [
              "7d",
              "30d"
            ],
            "description": "Trailing 7 x 24-hour or 30 x 24-hour case-creation window ending at snapshotAt, matching the synthetic dashboard. Start boundary is inclusive; only cases created at or before snapshotAt are eligible. Timezone controls display and separately agreed SLA-calendar calculations."
          },
          "department": {
            "type": "string",
            "enum": [
              "all",
              "merchant",
              "integrations",
              "settlement",
              "risk",
              "unassigned"
            ],
            "description": "Filter by accepted accountable department at snapshotAt; unassigned means no accepted owner. It is not a cause attribution."
          },
          "channel": {
            "type": "string",
            "enum": [
              "all",
              "whatsapp",
              "telegram",
              "web",
              "app"
            ],
            "description": "Filter by the single originating channel recorded when each case opened."
          },
          "cohortBasis": {
            "type": "string",
            "const": "case_created_at"
          },
          "channelAttribution": {
            "type": "string",
            "const": "originating_channel"
          },
          "projectionThrough": {
            "type": "string",
            "format": "date-time",
            "description": "Ingestion-time watermark: events accepted strictly before this point were evaluated for this revision. It does not imply every event was applied; unresolved events are counted in projectionIssues. It is not an occurredAt watermark."
          },
          "revision": {
            "type": "string",
            "description": "Opaque revision for consistent cached/report reads. A late event may revise the same historical cohort. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
          },
          "counts": {
            "$ref": "#/components/schemas/Counts"
          },
          "projectionIssues": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "pendingDependencies",
              "invalidTransitions"
            ],
            "properties": {
              "pendingDependencies": {
                "type": "integer",
                "minimum": 0
              },
              "invalidTransitions": {
                "type": "integer",
                "minimum": 0
              }
            },
            "description": "Aggregate events held for missing/out-of-order dependencies or transition investigation at this watermark. These are event counts, not additional cases. A later discovered conflict is surfaced here and in the authorized audit workflow; it never silently advances case state."
          },
          "channelBreakdown": {
            "type": "array",
            "maxItems": 4,
            "items": {
              "$ref": "#/components/schemas/ChannelBreakdown"
            },
            "description": "Optional mutually exclusive originating-channel groups over the same filtered cohort; when present, each count field sums to the summary count field. Include zero groups. No identifiers or raw personal data."
          },
          "departmentBreakdown": {
            "type": "array",
            "maxItems": 5,
            "items": {
              "$ref": "#/components/schemas/DepartmentBreakdown"
            },
            "description": "Optional mutually exclusive accepted-owner department groups over the same filtered cohort, including unassigned; when present each count field sums to the summary. No individual owner/customer identifiers."
          }
        },
        "description": "Aggregate reporting only. No raw conversations, account balances, customer records, evidence contents or personal data. Counts reconcile at the stated revision and watermark; late/out-of-order events can revise historical results."
      },
      "ChannelBreakdown": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "channel",
          "counts"
        ],
        "properties": {
          "channel": {
            "type": "string",
            "enum": [
              "whatsapp",
              "telegram",
              "web",
              "app"
            ]
          },
          "counts": {
            "$ref": "#/components/schemas/Counts"
          }
        }
      },
      "DepartmentBreakdown": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "department",
          "counts"
        ],
        "properties": {
          "department": {
            "type": "string",
            "enum": [
              "merchant",
              "integrations",
              "settlement",
              "risk",
              "unassigned"
            ]
          },
          "counts": {
            "$ref": "#/components/schemas/Counts"
          }
        }
      },
      "CaseOpened": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "eventId",
          "caseRef",
          "customerRef",
          "source",
          "occurredAt",
          "eventType",
          "data",
          "originatingChannel"
        ],
        "properties": {
          "eventId": {
            "type": "string",
            "format": "uuid",
            "description": "Globally unique source event identifier; stable across retries. Duplicate same-payload events must not advance state twice."
          },
          "caseRef": {
            "type": "string",
            "description": "Case reference within the credential tenant. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
          },
          "customerRef": {
            "type": "string",
            "description": "Customer reference within the credential tenant; no raw identity fields. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
          },
          "source": {
            "type": "string",
            "description": "Pre-authorized source system reference; credential policy must permit this source. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time",
            "description": "Source event time with explicit offset. Validate clock skew and retain original event time; receipt time is separately audited. Out-of-order arrival is never proof of an invalid financial outcome."
          },
          "eventType": {
            "type": "string",
            "const": "case.opened"
          },
          "data": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "initialStatus"
            ],
            "properties": {
              "initialStatus": {
                "type": "string",
                "const": "open"
              }
            }
          },
          "originatingChannel": {
            "type": "string",
            "enum": [
              "whatsapp",
              "telegram",
              "web",
              "app"
            ],
            "description": "Required only for case.opened. Remains the attribution for this case after any later channel handoff."
          }
        },
        "description": "Establishes the case-created cohort and immutable originating channel. A previously opened case cannot be reopened through a second opening event. Tenant is derived from the service credential and may not be supplied as a field."
      },
      "MessageReceived": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "eventId",
          "caseRef",
          "customerRef",
          "source",
          "occurredAt",
          "eventType",
          "data"
        ],
        "properties": {
          "eventId": {
            "type": "string",
            "format": "uuid",
            "description": "Globally unique source event identifier; stable across retries. Duplicate same-payload events must not advance state twice."
          },
          "caseRef": {
            "type": "string",
            "description": "Case reference within the credential tenant. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
          },
          "customerRef": {
            "type": "string",
            "description": "Customer reference within the credential tenant; no raw identity fields. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
          },
          "source": {
            "type": "string",
            "description": "Pre-authorized source system reference; credential policy must permit this source. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time",
            "description": "Source event time with explicit offset. Validate clock skew and retain original event time; receipt time is separately audited. Out-of-order arrival is never proof of an invalid financial outcome."
          },
          "eventType": {
            "type": "string",
            "const": "case.message_received"
          },
          "data": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "messageRef"
            ],
            "properties": {
              "messageRef": {
                "type": "string",
                "description": "Reference to a message retained in the authorized source system. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
                "minLength": 1,
                "maxLength": 128,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
              }
            }
          }
        },
        "description": "Records receipt metadata only. No message text, attachments, raw personal data or account details are accepted; this is not evidence of a human first reply or delivery. Tenant is derived from the service credential and may not be supplied as a field."
      },
      "OwnerAccepted": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "eventId",
          "caseRef",
          "customerRef",
          "source",
          "occurredAt",
          "eventType",
          "data"
        ],
        "properties": {
          "eventId": {
            "type": "string",
            "format": "uuid",
            "description": "Globally unique source event identifier; stable across retries. Duplicate same-payload events must not advance state twice."
          },
          "caseRef": {
            "type": "string",
            "description": "Case reference within the credential tenant. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
          },
          "customerRef": {
            "type": "string",
            "description": "Customer reference within the credential tenant; no raw identity fields. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
          },
          "source": {
            "type": "string",
            "description": "Pre-authorized source system reference; credential policy must permit this source. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time",
            "description": "Source event time with explicit offset. Validate clock skew and retain original event time; receipt time is separately audited. Out-of-order arrival is never proof of an invalid financial outcome."
          },
          "eventType": {
            "type": "string",
            "const": "case.owner_accepted"
          },
          "data": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "ownerRef",
              "department"
            ],
            "properties": {
              "ownerRef": {
                "type": "string",
                "description": "Authorized owner who accepted case responsibility. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
                "minLength": 1,
                "maxLength": 128,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
              },
              "department": {
                "type": "string",
                "enum": [
                  "merchant",
                  "integrations",
                  "settlement",
                  "risk"
                ],
                "description": "Accepted accountable department; unassigned is not a valid owner acceptance."
              }
            }
          }
        },
        "description": "Requires evidence of owner acceptance from an authorized source. Assignment suggestions do not establish accepted responsibility. Tenant is derived from the service credential and may not be supplied as a field."
      },
      "ResolutionVerified": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "eventId",
          "caseRef",
          "customerRef",
          "source",
          "occurredAt",
          "eventType",
          "data"
        ],
        "properties": {
          "eventId": {
            "type": "string",
            "format": "uuid",
            "description": "Globally unique source event identifier; stable across retries. Duplicate same-payload events must not advance state twice."
          },
          "caseRef": {
            "type": "string",
            "description": "Case reference within the credential tenant. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
          },
          "customerRef": {
            "type": "string",
            "description": "Customer reference within the credential tenant; no raw identity fields. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
          },
          "source": {
            "type": "string",
            "description": "Pre-authorized source system reference; credential policy must permit this source. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time",
            "description": "Source event time with explicit offset. Validate clock skew and retain original event time; receipt time is separately audited. Out-of-order arrival is never proof of an invalid financial outcome."
          },
          "eventType": {
            "type": "string",
            "const": "case.resolution_verified"
          },
          "data": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "evidenceRef",
              "authorityRef"
            ],
            "properties": {
              "evidenceRef": {
                "type": "string",
                "description": "Reference to independently verified resolution evidence in an authorized system. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
                "minLength": 1,
                "maxLength": 128,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
              },
              "authorityRef": {
                "type": "string",
                "description": "Reference to the authorized reviewer/action authority supporting resolution verification. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
                "minLength": 1,
                "maxLength": 128,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
              }
            }
          }
        },
        "description": "Requires verified resolution evidence and an authority reference that the credential can assert. AI-generated text, a sent response or an unverified provider status is insufficient. Recording a verified outcome never executes a refund, payout, credit decision, AML decision or account entry. Tenant is derived from the service credential and may not be supplied as a field."
      },
      "CaseEvent": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/CaseOpened"
          },
          {
            "$ref": "#/components/schemas/MessageReceived"
          },
          {
            "$ref": "#/components/schemas/OwnerAccepted"
          },
          {
            "$ref": "#/components/schemas/ResolutionVerified"
          }
        ],
        "discriminator": {
          "propertyName": "eventType",
          "mapping": {
            "case.opened": "#/components/schemas/CaseOpened",
            "case.message_received": "#/components/schemas/MessageReceived",
            "case.owner_accepted": "#/components/schemas/OwnerAccepted",
            "case.resolution_verified": "#/components/schemas/ResolutionVerified"
          }
        },
        "description": "One event-specific payload. Unknown properties are rejected; originatingChannel is allowed and required only on case.opened. References must resolve within the authenticated tenant. No command or financial action schema is accepted."
      },
      "SupportAlert": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "eventId",
          "caseRef",
          "eventType",
          "occurredAt",
          "revision"
        ],
        "properties": {
          "eventId": {
            "type": "string",
            "format": "uuid",
            "description": "Stable logical event identifier used for deduplication across redeliveries."
          },
          "caseRef": {
            "type": "string",
            "description": "Opaque authorized case reference. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
          },
          "eventType": {
            "type": "string",
            "enum": [
              "case.owner_changed",
              "case.sla_at_risk",
              "case.sla_breached",
              "case.resolution_verified"
            ]
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time"
          },
          "revision": {
            "type": "string",
            "description": "Projection revision that generated the alert. Opaque identifier only; never an account number, email, phone number, name or raw personal data.",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$"
          }
        },
        "description": "Minimal event notification only. Does not carry raw personal/account data or execute actions. A resolution alert is emitted only after evidence/authority validation and projection; it is not equivalent to a 202 ingestion receipt. Owner-change alerts reflect accepted ownership changes."
      }
    }
  }
}
