openapi: 3.1.0
info:
  title: Lerian Streaming Hub API
  version: 1.2.0
  contact:
    email: contact@lerian.studio
    name: Lerian Studio
    url: https://lerian.studio
  license:
    name: Lerian Studio General License
  description: >-
    The Streaming Hub control-plane API. Streaming Hub is Lerian's managed
    event-delivery edge: it consumes CloudEvents from the platform's internal
    streaming backbone and fans them out to a tenant's own external
    destinations — webhooks, Amazon SQS, RabbitMQ, Amazon EventBridge, or a pull
    inbox. This API lets a tenant browse the manifest-fed event catalog, create
    and manage delivery subscriptions, verify and rotate their credentials, read
    delivery health, and pull entitled events.


    An RFC 9457 `application/problem+json` error body carries `title` and
    `status`. An error the hub raises adds `type`, `detail`, and a stable
    low-cardinality `code` you can branch on; a request the framework rejects
    before the operation runs, such as a malformed or schema-invalid one,
    carries no `code`.
    Mutating operations require an `X-Idempotency` header for at-most-once
    semantics; a replayed request returns the original response byte-for-byte
    with `X-Idempotency-Replayed: true`. The catalog and the pull events surface
    are tenant-scoped through the bearer JWT; the operational probe endpoints
    (`/healthz`, `/readyz`, `/version`, `/runtime`, `/metrics`) are
    unauthenticated.
servers:
  - url: https://streaming-hub.sandbox.lerian.net
security:
  - BearerAuth: []
tags:
  - name: Catalog
    description: Browse the manifest-fed catalog of event types available for subscription.
  - name: Subscriptions
    description: >-
      Create, read, update, and delete delivery subscriptions, and drive the
      destination verification lifecycle (ping, verify, credential, delegated
      grant, secret rotation, health).
  - name: Event Delivery
    description: Pull entitled events for a pull-sink subscription (cursor-as-acknowledgment read).
  - name: Dashboard
    description: >-
      The read-only operational dashboard: a consistent snapshot of ingestion,
      push delivery, and pull eligibility, plus the paged list of subscriptions
      that need attention. It never consumes events and never returns a secret.
  - name: Admin
    description: Cross-tenant operator forensics. Requires an operator authorization scope.
  - name: Operational
    description: Unauthenticated liveness, readiness, build, runtime, and metrics probes.
paths:
  /v1/catalog:
    get:
      operationId: getCatalog
      summary: List the event catalog
      description: >-
        Returns the manifest-fed catalog snapshot — the last-known-good
        aggregation of the producer manifests Streaming Hub tracks. The catalog
        is global in v1 (byte-identical regardless of which tenant
        authenticates) and control-plane only; the data plane never consults it.
        An empty snapshot returns `200` with `events: []` (fail-closed to
        last-known-good — never an error on an empty catalog).
      tags:
        - Catalog
      security:
        - BearerAuth: []
      responses:
        "200":
          description: The catalog snapshot.
          headers:
            X-RateLimit-Limit:
              $ref: "#/components/headers/RateLimitLimit"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            X-RateLimit-Reset:
              $ref: "#/components/headers/RateLimitReset"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CatalogResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          $ref: "#/components/responses/MeteredServiceUnavailable"
        "504":
          $ref: "#/components/responses/GatewayTimeout"
      x-mint:
        href: /pt/reference/platform/streaming-hub/get-event-catalog
  /v1/subscriptions:
    post:
      operationId: createSubscription
      summary: Create a subscription
      description: >-
        Creates a delivery subscription for the authenticated tenant. A
        `tenant_id` in the body is ignored.


        A `webhook` subscription is born `pending_verification` and mints a
        signing secret that is returned in this response (see
        `signingSecret`); it stays undeliverable until a successful probe via
        `POST /v1/subscriptions/{id}/ping` moves it to `active`. A queue
        subscription (`sqs`, `rabbitmq`, `eventbridge`) is also born
        `pending_verification` and mints no secret; it stays undeliverable until
        a probe succeeds. Every queue kind activates on an outbound credential
        supplied and probed via `PUT /v1/subscriptions/{id}/credential`. An AWS
        kind (`sqs`, `eventbridge`) has a second path that stores no credential:
        register a delegated grant, then call
        `POST /v1/subscriptions/{id}/verify`. Inline `sink_config` or
        `credential` at create is rejected — queue credentials arrive only on the
        credential PUT.
      tags:
        - Subscriptions
      security:
        - BearerAuth: []
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateSubscriptionRequest"
      responses:
        "201":
          description: >-
            The subscription was created. `signingSecret` carries a secret only
            for a `webhook` sink. No read route returns it; a replay of the same
            `X-Idempotency` key returns this same response.
          headers:
            Cache-Control:
              $ref: "#/components/headers/NoStore"
            X-Idempotency-Replayed:
              $ref: "#/components/headers/IdempotencyReplayed"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateSubscriptionResponse"
        "400":
          $ref: "#/components/responses/BadRequestOrMissingIdempotency"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          description: >-
            Branch on `code`, not on the status. `subscription_name_taken`: a
            live subscription in your scope already holds this `name`, and
            `existing_id` names it, so you can reuse or delete it. A retry with
            the same name never succeeds. After you delete the holder, send the
            create with a new `X-Idempotency` key: the hub stores this refusal
            as the answer to the key that met it and replays it for that key.
            `idempotency_conflict`: the `X-Idempotency` key refuses this send,
            for example an in-flight duplicate or a key reused with a different
            request; nothing ran. `idempotency_outcome_unrecorded`: an earlier
            request under this key may have committed with no recorded outcome;
            read the resource before you send anything again.
          headers:
            X-Idempotency-Replayed:
              $ref: "#/components/headers/IdempotencyReplayed"
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/NameTakenError"
        "422":
          description: >-
            The request is caller-correctable. `code` names the fault, for
            example `validation_error` (a field failed validation),
            `inline_sink_config_forbidden` (an inline `sink_config` /
            `credential` was sent), `endpoint_blocked` (the webhook endpoint
            resolved to a blocked, private, or metadata address),
            `unknown_event_type` (an `event_types` value has no catalog entry at
            the schema major the subscription receives it at; each `errors`
            entry names one refused type and its major), or
            `event_type_outside_nature` (the delegated credential never receives
            an `event_types` value).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          description: >-
            The request could not be served now. `code` names the cause, for
            example `AUT-0033` (Access Manager did not decide the request) or
            `dns_unavailable` (the DNS
            lookup of the endpoint host failed; nothing was written, and the same
            `X-Idempotency` key can retry). Wait the `Retry-After` seconds, then
            retry.
          headers:
            Retry-After:
              $ref: "#/components/headers/RetryAfter"
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/Error"
        "504":
          description: >-
            A lookup did not finish in time. `code` names the cause, for example
            `AUT-1021` (Access Manager did not answer in time) or `dns_timeout`
            (the DNS lookup of the
            endpoint host timed out; nothing was written, and the same
            `X-Idempotency` key can retry).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/Error"
      x-mint:
        href: /pt/reference/platform/streaming-hub/create-subscription
    get:
      operationId: listSubscriptions
      summary: List subscriptions
      description: >-
        Lists the authenticated tenant's live subscriptions, newest-first,
        keyset-paginated. No read projection ever carries the signing secret.
        Each row carries `endpoint_display`, a short rendering of the
        destination; read the full destination with
        `GET /v1/subscriptions/{id}/endpoint`.
      tags:
        - Subscriptions
      security:
        - BearerAuth: []
      parameters:
        - name: sink_kind
          in: query
          required: false
          description: >-
            Optional filter restricting results to one sink kind. Any other value
            returns `422 validation_error`.
          schema:
            $ref: "#/components/schemas/SinkKind"
        - name: limit
          in: query
          required: false
          description: Page size, clamped to a server maximum. A non-positive value uses the server default.
          schema:
            type: integer
        - name: after
          in: query
          required: false
          description: >-
            Keyset cursor — the `id` of the last row from the previous page
            (subscriptions are ordered DESC by id). A value that is not a UUID
            returns `422 validation_error`.
          schema:
            type: string
        - name: name
          in: query
          required: false
          description: >-
            Optional exact-name filter, trimmed before matching. It matches in
            your scope, the same scope the create checks for a name conflict, so
            a hit is a page of one. A value that is only whitespace returns
            `422 validation_error`.
          schema:
            type: string
        - name: enabled
          in: query
          required: false
          description: >-
            Optional filter on the `enabled` flag, `true` or `false`. Any other
            value returns `422 validation_error`.
          schema:
            type: boolean
        - name: verification_state
          in: query
          required: false
          description: Optional filter on the verification state.
          schema:
            $ref: "#/components/schemas/VerificationState"
      responses:
        "200":
          description: A page of subscriptions.
          headers:
            X-RateLimit-Limit:
              $ref: "#/components/headers/RateLimitLimit"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            X-RateLimit-Reset:
              $ref: "#/components/headers/RateLimitReset"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListSubscriptionsResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "422":
          $ref: "#/components/responses/ValidationError"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          $ref: "#/components/responses/MeteredServiceUnavailable"
        "504":
          $ref: "#/components/responses/GatewayTimeout"
      x-mint:
        href: /pt/reference/platform/streaming-hub/list-subscriptions
  /v1/subscriptions/{id}:
    get:
      operationId: getSubscription
      summary: Get a subscription
      description: >-
        Returns one subscription scoped to the authenticated tenant. An absent,
        soft-deleted, or cross-tenant id returns a uniform `404 not_found` (no
        existence oracle). The response never carries the signing secret. It
        carries `endpoint_display`, not the full destination; read that with
        `GET /v1/subscriptions/{id}/endpoint`.
      tags:
        - Subscriptions
      security:
        - BearerAuth: []
      parameters:
        - $ref: "#/components/parameters/SubscriptionId"
      responses:
        "200":
          description: The subscription.
          headers:
            X-RateLimit-Limit:
              $ref: "#/components/headers/RateLimitLimit"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            X-RateLimit-Reset:
              $ref: "#/components/headers/RateLimitReset"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          $ref: "#/components/responses/MeteredServiceUnavailable"
        "504":
          $ref: "#/components/responses/GatewayTimeout"
      x-mint:
        href: /pt/reference/platform/streaming-hub/get-subscription
    delete:
      operationId: deleteSubscription
      summary: Delete a subscription
      description: >-
        Soft-deletes a subscription scoped to the authenticated tenant. The
        operation is idempotent: an absent, already-deleted, or cross-tenant id
        is a no-op that still answers `204` (no existence oracle).
      tags:
        - Subscriptions
      security:
        - BearerAuth: []
      parameters:
        - $ref: "#/components/parameters/SubscriptionId"
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "204":
          description: The subscription was deleted (or was already absent).
          headers:
            X-Idempotency-Replayed:
              $ref: "#/components/headers/IdempotencyReplayed"
        "400":
          $ref: "#/components/responses/BadRequestOrMissingIdempotency"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/IdempotencyConflict"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
        "504":
          $ref: "#/components/responses/GatewayTimeout"
      x-mint:
        href: /pt/reference/platform/streaming-hub/delete-subscription
    patch:
      operationId: updateSubscriptionSchemaMajor
      summary: Re-pin a subscription's schema major
      description: >-
        Narrowly changes a subscription's `schema_major` pin and, optionally,
        its per-attempt delivery timeout. Every call carries `schema_major`: an
        integer `>= 1` re-pins; `null` clears the pin, so the subscription
        follows major 1. `timeout_ms` is optional; omit it to keep the stored
        timeout. Any other field, an out-of-range value, or an empty body is
        rejected with `422 validation_error` (out-of-scope fields are rejected,
        not ignored). A pin that leaves a stored event type with no catalog
        entry at its major is rejected with `422 unknown_event_type`. The change is not a destination change: it does not reset
        the verification state and echoes no secret. Success returns `200` with
        no body.
      tags:
        - Subscriptions
      security:
        - BearerAuth: []
      parameters:
        - $ref: "#/components/parameters/SubscriptionId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PatchSubscriptionRequest"
      responses:
        "200":
          description: The re-pin succeeded (no body).
          headers:
            X-Idempotency-Replayed:
              $ref: "#/components/headers/IdempotencyReplayed"
        "400":
          $ref: "#/components/responses/BadRequestOrMissingIdempotency"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/IdempotencyConflict"
        "422":
          description: >-
            The request is caller-correctable. `code` is `validation_error` (a
            field failed validation) or `unknown_event_type` (the pin leaves a
            stored event type with no catalog entry at its major; each `errors`
            entry names one refused type and its major).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
        "504":
          $ref: "#/components/responses/GatewayTimeout"
      x-mint:
        href: /pt/reference/platform/streaming-hub/update-subscription-schema-pin
  /v1/subscriptions/{id}/endpoint:
    get:
      operationId: revealSubscriptionEndpoint
      summary: Read a subscription's full destination
      description: >-
        Returns the full, unmasked delivery destination of one subscription of
        the authenticated tenant. The list and detail routes return only
        `endpoint_display`. Authorization checks the resource `subscriptions`
        and the action `post`, the same permission that creates a
        subscription. An absent, soft-deleted, or cross-tenant id returns the
        same uniform `404 not_found` as the detail route.
      tags:
        - Subscriptions
      security:
        - BearerAuth: []
      parameters:
        - $ref: "#/components/parameters/SubscriptionId"
      responses:
        "200":
          description: The full destination.
          headers:
            Cache-Control:
              $ref: "#/components/headers/NoStore"
            X-RateLimit-Limit:
              $ref: "#/components/headers/RateLimitLimit"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            X-RateLimit-Reset:
              $ref: "#/components/headers/RateLimitReset"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EndpointResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          $ref: "#/components/responses/MeteredServiceUnavailable"
        "504":
          $ref: "#/components/responses/GatewayTimeout"
      x-mint:
        href: /pt/reference/platform/streaming-hub/get-subscription-endpoint
  /v1/subscriptions/{id}/secret/rotate:
    post:
      operationId: rotateSubscriptionSecret
      summary: Rotate the signing secret
      description: >-
        Mints a new webhook signing secret, returns it, persists it
        as the current secret, and demotes the previous secret with a 24-hour
        overlap. During the overlap, each webhook delivery also carries a
        signature made with the previous secret. A subscription with no signing secret at rest (a
        `pull` sink) returns `422 no_secret_to_rotate`.
      tags:
        - Subscriptions
      security:
        - BearerAuth: []
      parameters:
        - $ref: "#/components/parameters/SubscriptionId"
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "200":
          description: >-
            A new signing secret was minted. No read route returns it; a replay of
            the same `X-Idempotency` key returns this same response.
          headers:
            Cache-Control:
              $ref: "#/components/headers/NoStore"
            X-Idempotency-Replayed:
              $ref: "#/components/headers/IdempotencyReplayed"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RotateSecretResponse"
        "400":
          $ref: "#/components/responses/BadRequestOrMissingIdempotency"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/IdempotencyConflict"
        "422":
          description: The subscription has no signing secret to rotate — `code` is `no_secret_to_rotate`.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
        "504":
          $ref: "#/components/responses/GatewayTimeout"
      x-mint:
        href: /pt/reference/platform/streaming-hub/rotate-subscription-secret
  /v1/subscriptions/{id}/ping:
    post:
      operationId: pingSubscription
      summary: Ping a webhook subscription
      description: >-
        Runs a synchronous, signed webhook probe through the production delivery
        path and returns the classified result. A successful ping flips a
        `webhook` subscription from `pending_verification` to `active`. A
        classified probe failure is still a `200` (the probe ran and produced a
        verdict). This route is naturally idempotent and requires no
        `X-Idempotency` header. A queue subscription has no ping probe and
        returns `422 probe_unsupported_for_sink_kind`.
      tags:
        - Subscriptions
      security:
        - BearerAuth: []
      parameters:
        - $ref: "#/components/parameters/SubscriptionId"
      responses:
        "200":
          description: The probe ran; the body carries the classified attempt result.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProbeResult"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ProbeUnsupported"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
        "504":
          $ref: "#/components/responses/GatewayTimeout"
      x-mint:
        href: /pt/reference/platform/streaming-hub/ping-subscription
  /v1/subscriptions/{id}/verify:
    post:
      operationId: verifySubscription
      summary: Re-verify and re-enable a subscription
      description: >-
        Re-runs the sink-appropriate probe (dispatched by sink kind) and, on a
        probe success, transitions the subscription back to `active` and clears
        any auto-disable in the same transaction. The row is transitioned in
        place — the id and signing secret are preserved. A classified probe
        failure is still a `200`. This route is naturally idempotent and requires
        no `X-Idempotency` header. The `webhook`, `sqs` and `eventbridge` sinks
        have registered verify probes. For an AWS sink this is the
        delegated-grant activation path, the alternative to an outbound
        credential on `PUT /v1/subscriptions/{id}/credential`, which probes every
        queue kind at write time. Any other sink kind returns
        `422 probe_unsupported_for_sink_kind`: a `rabbitmq` sink is probed on that
        credential PUT, which moves `verification_state` alone, and a `pull`
        subscription has no destination to probe.
      tags:
        - Subscriptions
      security:
        - BearerAuth: []
      parameters:
        - $ref: "#/components/parameters/SubscriptionId"
      responses:
        "200":
          description: The probe ran; the body carries the classified result.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProbeResult"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ProbeUnsupported"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
        "504":
          $ref: "#/components/responses/GatewayTimeout"
      x-mint:
        href: /pt/reference/platform/streaming-hub/verify-subscription
  /v1/subscriptions/{id}/credential:
    put:
      operationId: putSubscriptionCredential
      summary: Set or rotate a queue credential
      description: >-
        Sets or rotates the outbound credential for a queue subscription (`sqs`,
        `rabbitmq`, `eventbridge`) with probe-on-write: the candidate credential
        is held in memory, probed (connect + auth) immediately, and persisted
        encrypted only on probe success — a failed probe stores nothing. A
        successful probe sets `verification_state` to `active`: from
        `pending_verification` on the first write, and from `degraded` when a
        rotation fixes a destination that had started failing. It leaves the
        `enabled` flag exactly as it found it.
        The credential is write-only: it is supplied only here and is
        never returned, not even masked, on any read path. A classified probe
        failure is still a `200`. This route is naturally idempotent and requires
        no `X-Idempotency` header.
      tags:
        - Subscriptions
      security:
        - BearerAuth: []
      parameters:
        - $ref: "#/components/parameters/SubscriptionId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CredentialRequest"
      responses:
        "200":
          description: >-
            The probe ran; the body carries the classified result. On success
            the credential was persisted and the subscription is `active`; on a
            classified failure nothing was persisted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProbeResult"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          description: >-
            The request is caller-correctable. `code` names the fault, for
            example `validation_error` (an absent or empty `sinkConfig`) or
            `endpoint_blocked` (the broker host resolved to a blocked, private,
            or metadata address).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
        "504":
          $ref: "#/components/responses/GatewayTimeout"
      x-mint:
        href: /pt/reference/platform/streaming-hub/set-subscription-credential
  /v1/subscriptions/{id}/setup-artifacts:
    get:
      operationId: getSubscriptionSetupArtifacts
      summary: Get AWS delegated-grant setup artifacts
      description: >-
        Generates the AWS delegated-grant onboarding artifacts for an AWS-sink
        subscription (`sqs`, `eventbridge`): the cross-account IAM trust policy
        and a CloudFormation quick-create console link the customer applies in
        their own AWS account to create the delivery role Streaming Hub assumes.
        On first fetch a non-secret `sts:ExternalId` correlation value is minted
        and embedded in both artifacts; a re-fetch reuses it (stable artifacts).
        No AWS credential is ever stored or returned — the role is assumed per
        delivery, guarded by the `ExternalId` condition. A non-AWS sink returns
        `422 validation_error`.
      tags:
        - Subscriptions
      security:
        - BearerAuth: []
      parameters:
        - $ref: "#/components/parameters/SubscriptionId"
      responses:
        "200":
          description: The delegated-grant artifacts.
          headers:
            X-RateLimit-Limit:
              $ref: "#/components/headers/RateLimitLimit"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            X-RateLimit-Reset:
              $ref: "#/components/headers/RateLimitReset"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SetupArtifactsResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ValidationError"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          description: >-
            An infrastructure fault, or the deployment has not configured the
            hub principal ARN / setup template URL required to render the
            artifacts. `code` is `internal_error`.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/Error"
        "503":
          $ref: "#/components/responses/MeteredServiceUnavailable"
        "504":
          $ref: "#/components/responses/GatewayTimeout"
      x-mint:
        href: /pt/reference/platform/streaming-hub/get-subscription-setup-artifacts
  /v1/subscriptions/{id}/delegated-grant:
    put:
      operationId: putSubscriptionDelegatedGrant
      summary: Register an AWS delegated grant
      description: >-
        Registers the AWS delegated-grant coordinates for an AWS-sink
        subscription (`sqs`, `eventbridge`) — the non-secret cross-account
        delivery role ARN, region, and destination the hub uses at delivery
        time. This is the write that follows the customer applying the
        setup-artifacts trust policy in their AWS account. It is write-only: it
        runs no inline probe and does not change the verification state (a
        registered grant stays `pending_verification` until a later `verify`).
        No AWS credential crosses this body. The destination host is validated
        before the write. A non-AWS sink returns `422 validation_error`.
      tags:
        - Subscriptions
      security:
        - BearerAuth: []
      parameters:
        - $ref: "#/components/parameters/SubscriptionId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DelegatedGrantRequest"
      responses:
        "200":
          description: The grant was persisted (no body; the verification state is unchanged).
          headers:
            X-Idempotency-Replayed:
              $ref: "#/components/headers/IdempotencyReplayed"
        "400":
          $ref: "#/components/responses/BadRequestOrMissingIdempotency"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/IdempotencyConflict"
        "422":
          description: >-
            The request is caller-correctable. `code` names the fault, for
            example `validation_error` (a missing `roleArn` / `region` /
            `destination`, or a non-AWS sink) or `endpoint_blocked` (the
            destination host resolved to a blocked, private, or metadata
            address).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
        "504":
          $ref: "#/components/responses/GatewayTimeout"
      x-mint:
        href: /pt/reference/platform/streaming-hub/set-subscription-delegated-grant
  /v1/subscriptions/{id}/health:
    get:
      operationId: getSubscriptionHealth
      summary: Get subscription delivery health
      description: >-
        Returns a consolidated, non-secret delivery-health rollup for one
        subscription — the "why did this tenant stop receiving events" view. The
        existence gate runs first, so an absent, soft-deleted, or cross-tenant id
        returns a uniform `404 not_found`. The counts cover the measured
        interval `[window_start, snapshot_at)`. The auto-disable verdict and the
        last success and failure are reported whenever they happened.
      tags:
        - Subscriptions
      security:
        - BearerAuth: []
      parameters:
        - $ref: "#/components/parameters/SubscriptionId"
      responses:
        "200":
          description: The delivery-health rollup.
          headers:
            X-RateLimit-Limit:
              $ref: "#/components/headers/RateLimitLimit"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            X-RateLimit-Reset:
              $ref: "#/components/headers/RateLimitReset"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionHealth"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          $ref: "#/components/responses/MeteredServiceUnavailable"
        "504":
          $ref: "#/components/responses/GatewayTimeout"
      x-mint:
        href: /pt/reference/platform/streaming-hub/get-subscription-health
  /v1/events:
    get:
      operationId: pullEvents
      summary: Pull entitled events
      description: >-
        Fetches a page of a pull subscription's entitled events in arrival-order
        `seq`. The read doubles as the acknowledgment (cursor-as-ack): after a
        successful fetch the highest returned `seq` is persisted as the
        subscription's cursor, monotonically. An explicit `after` cursor
        overrides the persisted cursor for the read; a forward seek advances the
        durable cursor past the unread gap (the skipped range is not re-delivered
        — caller-owns-the-gap), while a backward seek never rewinds it. The read
        is rate-limited per tenant. Events are at-least-once; deduplicate on
        `ceId`.
      tags:
        - Event Delivery
      security:
        - BearerAuth: []
      parameters:
        - name: subscription_id
          in: query
          required: true
          description: >-
            The pull subscription to read. Resolved through an existence gate
            then a kind gate: an absent, cross-tenant, or non-`pull` id all
            return a uniform `404 not_found` (no existence or kind oracle).
          schema:
            type: string
            format: uuid
        - name: after
          in: query
          required: false
          description: >-
            Explicit keyset cursor (replay). When present it overrides the
            persisted cursor for the read; absent it, the read resumes from the
            persisted acknowledged `seq` (0 = from the beginning). A malformed or
            negative value is ignored and folds to the persisted cursor.
          schema:
            type: integer
            format: int64
        - name: limit
          in: query
          required: false
          description: Page size, clamped to the range 1–500, defaulting to 100.
          schema:
            type: integer
      responses:
        "200":
          description: A page of events in ascending `seq` order.
          headers:
            X-RateLimit-Limit:
              $ref: "#/components/headers/RateLimitLimit"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            X-RateLimit-Reset:
              $ref: "#/components/headers/RateLimitReset"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PullEventsResponse"
        "400":
          description: The required `subscription_id` query parameter is missing — `code` is `bad_request`.
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          $ref: "#/components/responses/MeteredServiceUnavailable"
        "504":
          $ref: "#/components/responses/GatewayTimeout"
      x-mint:
        href: /pt/reference/platform/streaming-hub/pull-events
  /v1/dashboard:
    get:
      operationId: getDashboard
      summary: Get the operational dashboard snapshot
      description: >-
        Returns one consistent read of the tenant's hub: a point-in-time
        subscription census and push backlog, plus ingestion, push-delivery,
        and pull-eligibility counts over the selected period, with a time
        series. The period is `[from, to)`, or the rolling `window_hours`
        width that ends at the hub's snapshot instant. A count is `0` when
        nothing matched; `null` means the fact does not exist. This route
        never consumes events and never moves a pull cursor. Authorization
        checks the resource `dashboard` and the action `get`.
      tags:
        - Dashboard
      security:
        - BearerAuth: []
      parameters:
        - name: from
          in: query
          required: false
          description: >-
            The inclusive start of the period, an RFC 3339 instant. Defaults to
            the resolved `to` minus 24 hours.
          schema:
            type: string
            format: date-time
        - name: to
          in: query
          required: false
          description: >-
            The exclusive end of the period, an RFC 3339 instant. Defaults to
            the snapshot instant; a later value is rejected. The period spans
            at most 30 days.
          schema:
            type: string
            format: date-time
        - name: window_hours
          in: query
          required: false
          description: >-
            A rolling period width in whole hours, from `1` to `720`: the
            window `[snapshot_at - window_hours, snapshot_at)` on the hub's
            clock. Use it for a preset period, such as the last 24 hours. It
            cannot be sent with `from` or `to`. To read the attention list over
            the same period, send this response's `window_start` and
            `window_end` to `GET /v1/dashboard/subscriptions` as `from` and
            `to`.
          schema:
            type: integer
      responses:
        "200":
          description: The dashboard snapshot.
          headers:
            Cache-Control:
              $ref: "#/components/headers/NoStore"
            X-RateLimit-Limit:
              $ref: "#/components/headers/RateLimitLimit"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            X-RateLimit-Reset:
              $ref: "#/components/headers/RateLimitReset"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DashboardSnapshot"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "422":
          $ref: "#/components/responses/DashboardValidationError"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          $ref: "#/components/responses/MeteredServiceUnavailable"
        "504":
          $ref: "#/components/responses/GatewayTimeout"
      x-mint:
        href: /pt/reference/platform/streaming-hub/get-dashboard
  /v1/dashboard/subscriptions:
    get:
      operationId: getDashboardNeedsAttention
      summary: List the subscriptions needing attention
      description: >-
        Returns one keyset page of the tenant's subscriptions that need an
        operator's attention, ordered by attention rank, then most recent
        failure, then id. Each item carries only non-secret operational state
        and the period's attempt counts: never an endpoint, credential, signing
        secret, or raw error. To read the next page, send `next_cursor` as
        `cursor` with the same `from` and `to`. Authorization checks the
        resource `dashboard` and the action `get`.
      tags:
        - Dashboard
      security:
        - BearerAuth: []
      parameters:
        - name: from
          in: query
          required: false
          description: The inclusive start of the period, an RFC 3339 instant. Required when `cursor` is present.
          schema:
            type: string
            format: date-time
        - name: to
          in: query
          required: false
          description: The exclusive end of the period, an RFC 3339 instant. Required when `cursor` is present.
          schema:
            type: string
            format: date-time
        - name: window_hours
          in: query
          required: false
          description: >-
            A rolling period width in whole hours, from `1` to `720`, resolved
            against this request's own snapshot instant. It cannot be sent with
            `from` or `to`, and it applies to the first page only: a page with
            `cursor` repeats the first page's `window_start` and `window_end` as
            `from` and `to`.
          schema:
            type: integer
        - name: limit
          in: query
          required: false
          description: The page size, from `1` to `50`. Defaults to `20`. A value out of range is rejected, not clamped.
          schema:
            type: integer
        - name: cursor
          in: query
          required: false
          description: >-
            The `next_cursor` of the previous page. It is signed and bound to
            the period and to the caller, so repeat `from` and `to` unchanged.
          schema:
            type: string
      responses:
        "200":
          description: A page of subscriptions needing attention.
          headers:
            Cache-Control:
              $ref: "#/components/headers/NoStore"
            X-RateLimit-Limit:
              $ref: "#/components/headers/RateLimitLimit"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            X-RateLimit-Reset:
              $ref: "#/components/headers/RateLimitReset"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NeedsAttentionPage"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "422":
          $ref: "#/components/responses/DashboardValidationError"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          $ref: "#/components/responses/MeteredServiceUnavailable"
        "504":
          $ref: "#/components/responses/GatewayTimeout"
      x-mint:
        href: /pt/reference/platform/streaming-hub/list-subscriptions-needing-attention
  /admin/dlq:
    get:
      operationId: listDLQObservations
      summary: List dead-letter observations
      description: >-
        Cross-tenant operator forensics: returns a keyset-paginated,
        reverse-chronological page of dead-letter observations. The projection
        carries no secret, no customer endpoint URL, and no signing material. The
        `tenant_id_raw` field is an untrusted, best-effort capture surfaced for
        forensic value only.
      tags:
        - Admin
      security:
        - BearerAuth: []
      parameters:
        - name: source_topic
          in: query
          required: false
          description: >-
            Optional filter on the topic the failed record arrived on (exact
            match), or `unknown` for records whose origin the hub could not
            read. A value that is not a topic name returns
            `400 invalid_source_topic`.
          schema:
            type: string
        - name: error_class
          in: query
          required: false
          description: Optional filter on the transport error class. An unrecognized value returns `400 invalid_error_class`.
          schema:
            $ref: "#/components/schemas/DLQErrorClass"
        - name: after
          in: query
          required: false
          description: Keyset cursor — the `id` (UUIDv7) of the last row from the previous page. A malformed value returns `400 invalid_cursor`.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: A page of dead-letter observations.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DLQResponse"
        "400":
          description: >-
            A filter value was invalid. `code` is `invalid_error_class` (an
            unrecognized `error_class`), `invalid_source_topic` (a
            `source_topic` that is not a topic name), or `invalid_cursor` (a
            malformed `after`).
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
        "504":
          $ref: "#/components/responses/GatewayTimeout"
      x-mint:
        href: /pt/reference/platform/streaming-hub/list-dlq-observations
  /healthz:
    get:
      operationId: getLiveness
      summary: Liveness probe
      description: >-
        Returns `200` unconditionally once the process is serving, independent of
        dependency health. Unauthenticated.
      tags:
        - Operational
      security: []
      responses:
        "200":
          description: The process is alive.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LivenessResponse"
      x-mint:
        href: /pt/reference/platform/streaming-hub/liveness-probe
  /readyz:
    get:
      operationId: getReadiness
      summary: Readiness probe
      description: >-
        Folds the full probe set for the replica into one readiness
        verdict. `Healthy` and `Degraded` return `200` (kept in rotation);
        `Down` returns `503`. Unauthenticated.
      tags:
        - Operational
      security: []
      responses:
        "200":
          description: The replica is ready (state `healthy` or `degraded`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReadinessResponse"
        "503":
          description: The replica is not ready (state `down`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReadinessResponse"
      x-mint:
        href: /pt/reference/platform/streaming-hub/readiness-probe
  /version:
    get:
      operationId: getVersion
      summary: Build identity
      description: Returns the build identity injected at link time. Unauthenticated.
      tags:
        - Operational
      security: []
      responses:
        "200":
          description: The build identity.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VersionResponse"
      x-mint:
        href: /pt/reference/platform/streaming-hub/get-version
  /runtime:
    get:
      operationId: getRuntime
      summary: Runtime snapshot
      description: >-
        Returns a cheap Go-runtime snapshot for incident triage (goroutine count,
        GC cycles, heap bytes). No tenant or high-cardinality data. Unauthenticated.
      tags:
        - Operational
      security: []
      responses:
        "200":
          description: The runtime snapshot.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RuntimeResponse"
      x-mint:
        href: /pt/reference/platform/streaming-hub/get-runtime
  /metrics:
    get:
      operationId: getMetrics
      summary: Prometheus metrics
      description: >-
        Serves a Prometheus text-exposition document. Streaming Hub exports its
        runtime metrics over OTLP, so this endpoint serves only the static
        `streaming_hub_build_info` gauge. Unauthenticated.
      tags:
        - Operational
      security: []
      responses:
        "200":
          description: A Prometheus text-exposition document.
          content:
            text/plain:
              schema:
                type: string
      x-mint:
        href: /pt/reference/platform/streaming-hub/get-metrics
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        A bearer JWT issued by Access Manager. The `/v1` surface never reads a
        tenant from the body, path, or query. Machine callers obtain a token with the
        Access Manager client-credentials flow. The `/admin` surface authorizes
        against an operator scope and carries no tenant context.
  parameters:
    SubscriptionId:
      name: id
      in: path
      required: true
      description: The unique identifier of the subscription (UUIDv7).
      schema:
        type: string
        format: uuid
    IdempotencyKey:
      name: X-Idempotency
      in: header
      required: true
      description: >-
        A client-chosen unique key that makes this mutation at-most-once. A
        mutation sent without it is rejected before any write with
        `400 missing_idempotency_key`. Reusing the same key with an identical
        request replays the original response byte-for-byte (with
        `X-Idempotency-Replayed: true`); reusing it with a different request body
        returns `409 idempotency_conflict`. To re-drive a corrected request, mint
        a new key.
      schema:
        type: string
  headers:
    IdempotencyReplayed:
      description: >-
        Present and set to `true` when this response is a replay of a previously
        committed request carrying the same `X-Idempotency` key (the handler did
        not run again).
      schema:
        type: string
        enum:
          - "true"
    NoStore:
      description: Always `private, no-store`. Do not cache this response.
      schema:
        type: string
        enum:
          - private, no-store
    RateLimitLimit:
      description: The requests this tenant may make in one window of this route's allowance.
      schema:
        type: integer
        examples:
          - 60
    RateLimitRemaining:
      description: The requests left in the current window. `0` on a refusal.
      schema:
        type: integer
        examples:
          - 59
    RateLimitReset:
      description: The Unix time, in seconds, at which the current window ends.
      schema:
        type: integer
        examples:
          - 1789499562
    RetryAfter:
      description: The whole seconds to wait before a retry. Always at least `1`.
      schema:
        type: string
        examples:
          - "1"
  responses:
    BadRequest:
      description: The request body is malformed — `code` is `bad_request`.
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Error"
    BadRequestOrMissingIdempotency:
      description: >-
        The request body is malformed (`bad_request`) or the required
        `X-Idempotency` header is missing (`missing_idempotency_key`, rejected
        before any write).
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Error"
    Unauthorized:
      description: >-
        Authentication failed. `code` names the cause, for example `AUT-0006`
        (no token), `AUT-0007` (an expired, invalid, or malformed token), or
        `unauthorized` (no trusted tenant context).
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Error"
    Forbidden:
      description: >-
        The caller is authenticated but is not allowed to make this request.
        `code` names the cause, for example `AUT-1020` (the credential does not
        have the permission; `detail` names the refused `resource:action`) or
        `forbidden`.
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Error"
    NotFound:
      description: >-
        The resource is absent, soft-deleted, or owned by another tenant — a
        uniform `code` of `not_found` (no existence oracle).
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Error"
    ValidationError:
      description: A caller-correctable shape fault — `code` is `validation_error`.
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Error"
    DashboardValidationError:
      description: >-
        A query parameter is invalid. `code` is `validation_error`. When the
        period starts before the retained history, `details` carries
        `coverage_start` and `snapshot_at`, so you can choose a period the hub
        can serve.
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: "#/components/schemas/Error"
              - type: object
                properties:
                  details:
                    $ref: "#/components/schemas/CoverageBounds"
    ProbeUnsupported:
      description: >-
        The sink kind has no registered probe for this operation — `code` is
        `probe_unsupported_for_sink_kind`.
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Error"
    IdempotencyConflict:
      description: >-
        An in-flight duplicate, or the same `X-Idempotency` key was reused with a
        different request fingerprint — `code` is `idempotency_conflict`. When an
        earlier request under this key may have committed with no recorded
        outcome, `code` is `idempotency_outcome_unrecorded`: read the resource
        before you send anything again.
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Error"
    InternalError:
      description: >-
        An infrastructure fault. `code` is `internal_error` and `detail` is
        scrubbed to the static `"internal error"` (the cause is logged, never
        returned). On a mutation that requires `X-Idempotency`, `code` can
        instead be `idempotency_outcome_unrecorded`: the mutation may have
        committed, and its outcome was not recorded. Read the resource before
        you send anything again.
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Error"
    RateLimited:
      description: >-
        The tenant's allowance for this route is spent. `code` is
        `rate_limited`. Wait `Retry-After` seconds, then retry. Each tenant has
        three allowances: one for the catalog and the subscription list,
        detail, endpoint, health, and setup-artifacts reads; one for
        `GET /v1/events`; and one for the two dashboard routes.
      headers:
        Retry-After:
          $ref: "#/components/headers/RetryAfter"
        X-RateLimit-Limit:
          $ref: "#/components/headers/RateLimitLimit"
        X-RateLimit-Remaining:
          $ref: "#/components/headers/RateLimitRemaining"
        X-RateLimit-Reset:
          $ref: "#/components/headers/RateLimitReset"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Error"
    ServiceUnavailable:
      description: >-
        Access Manager did not decide the request. `code` names the cause, for
        example `AUT-0033`. This is a transient condition, not a denial: wait the `Retry-After` seconds, then
        retry.
      headers:
        Retry-After:
          $ref: "#/components/headers/RetryAfter"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Error"
    GatewayTimeout:
      description: >-
        Access Manager did not answer in time. `code` names the cause, for
        example `AUT-1021`. Retry the request.
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Error"
    MeteredServiceUnavailable:
      description: >-
        The hub cannot serve the request now. Branch on `code`. `AUT-0033`:
        Access Manager did not decide the request. `rate_limiter_unavailable`:
        the hub cannot check the tenant's allowance.
        `delegated_party_unconfirmed`, on
        `GET /v1/events` only: the hub cannot yet confirm the delegated party of
        the subscription. `read_timeout`, on the dashboard reads and the
        delivery-health read only: the read ran out of its time budget. Each is
        a transient condition, not a denial or a quota. Wait the `Retry-After`
        seconds, then retry; `read_timeout` carries no `Retry-After`.
      headers:
        Retry-After:
          $ref: "#/components/headers/RetryAfter"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Error"
  schemas:
    Error:
      type: object
      description: >-
        RFC 9457 `application/problem+json` document. An error the hub raises
        carries `detail` and the stable `code` a client branches on; a request
        the framework rejects before the operation runs carries no `code`.
      properties:
        type:
          type: string
          format: uri
          description: >-
            A stable, versioned URI identifying the problem type
            (`https://errors.lerian.studio/v1/<code>`).
          examples:
            - https://errors.lerian.studio/v1/not_found
        title:
          type: string
          description: A short human-readable summary — the HTTP status text.
          examples:
            - Not Found
        status:
          type: integer
          description: The HTTP status code, mirrored in the body.
          examples:
            - 404
        detail:
          type: string
          description: A human explanation of this specific occurrence.
          examples:
            - subscription not found
        code:
          type: string
          description: >-
            The stable, low-cardinality, machine-readable token a client
            branches on (for example `not_found`, `unauthorized`,
            `idempotency_conflict`, `validation_error`). Absent when the
            framework rejects the request before the operation runs.
          examples:
            - not_found
        instance:
          type: string
          description: The trace id of the request, when the hub has one.
          examples:
            - 4bf92f3577b34da6a3ce929d0e0e4736
        errors:
          type: array
          description: >-
            When present, the entries name what was refused. On
            `unknown_event_type`, each entry names one refused event type and
            its schema major.
          items:
            type: object
            properties:
              location:
                type: string
                description: The refused field, with its position in a list.
                examples:
                  - event_types/2
              message:
                type: string
                description: Why the hub refused the value.
                examples:
                  - '"transaction.creatd" has no catalog entry at schema major 1'
      required:
        - title
        - status
    NameTakenError:
      description: >-
        The `409` body of a create: the `Error` document, plus `existing_id`
        when `code` is `subscription_name_taken`.
      allOf:
        - $ref: "#/components/schemas/Error"
        - type: object
          properties:
            existing_id:
              type: string
              format: uuid
              description: >-
                The id of the subscription in your scope that held the name when
                the hub refused the create. Absent only when that subscription
                was deleted before the hub looked up its id. A replayed refusal
                can name a subscription deleted since.
              examples:
                - 0192f1a0-0000-7000-8000-0000000000ff
    CoverageBounds:
      type: object
      description: The retained-history bounds of a refused dashboard period.
      properties:
        coverage_start:
          type: string
          format: date-time
          description: >-
            The earliest instant of the uninterrupted retained history (UTC). A
            period that starts at or after it can be served.
          examples:
            - "2026-09-04T00:00:00Z"
        snapshot_at:
          type: string
          format: date-time
          description: The database instant the refusing read observed (UTC).
          examples:
            - "2026-09-11T12:00:00Z"
      required:
        - coverage_start
        - snapshot_at
    SinkKind:
      type: string
      description: >-
        The delivery destination kind. `webhook` posts signed HTTPS requests;
        `pull` exposes an inbox read over `GET /v1/events`; `sqs`, `rabbitmq`,
        and `eventbridge` fan out to the named queue or bus.
      enum:
        - webhook
        - pull
        - sqs
        - rabbitmq
        - eventbridge
    VerificationState:
      type: string
      description: >-
        The subscription's position in the destination verification state
        machine. Only an `active` subscription is deliverable. A `pull`
        subscription is born `active` — it has no destination to probe. Every
        other kind is born `pending_verification` and reaches `active` on a
        successful probe: `POST /v1/subscriptions/{id}/ping` for a `webhook`,
        and `PUT /v1/subscriptions/{id}/credential` for any queue sink (`sqs`,
        `rabbitmq`, `eventbridge`). An `sqs` or `eventbridge` sink has a second
        path: register a delegated grant — which persists the coordinates and
        leaves this field unchanged — then call
        `POST /v1/subscriptions/{id}/verify`. An `active` subscription whose probe
        later fails becomes `degraded`. The same successful probe returns this
        field to `active`. This field is one half of deliverability: `enabled` is
        the other half, and both must hold. No probe changes `enabled` except
        `POST /v1/subscriptions/{id}/verify`, which on a probe success clears an
        auto-disable in the same transaction as the state move.
      enum:
        - pending_verification
        - active
        - degraded
    Outcome:
      type: string
      description: The classified verdict of a synchronous probe.
      enum:
        - ok
        - failed
    HealthStatus:
      type: string
      description: >-
        The rolled-up delivery-health verdict. `Down` means the subscription was
        auto-disabled by the system (the endpoint is judged broken); `Degraded`
        means a lesser impairment (a manual disable, dead-lettered attempts, or
        dropped tenant events); `Healthy` otherwise.
      enum:
        - Healthy
        - Degraded
        - Down
    DLQErrorClass:
      type: string
      description: The transport error class a dead-letter observation may be filtered on.
      enum:
        - serialization
        - validation
        - auth
        - topic_not_found
        - broker_unavailable
        - network_timeout
        - broker_overloaded
        - context_canceled
        - unknown
    CreateSubscriptionRequest:
      type: object
      description: >-
        The create body. It carries no `tenant_id` (the tenant is resolved from
        the JWT). Inline `sink_config` / `credential` fields are rejected with
        `422 inline_sink_config_forbidden` — queue credentials arrive only via
        the credential PUT.
      properties:
        name:
          type: string
          description: A human label for the subscription. Required and non-blank (whitespace-only is rejected).
          examples:
            - orders-webhook
        sink_kind:
          $ref: "#/components/schemas/SinkKind"
        endpoint:
          type: string
          description: >-
            The destination address, interpreted per sink kind. For `webhook`, an
            `https://` URL with no embedded userinfo. For `pull`, omit it — the
            server synthesizes a `pull://<id>` value. For `sqs`, the `https://`
            queue URL. For `rabbitmq`, an `"<exchange>/<routingKey>"` string
            (exchange required, routing key optional). For `eventbridge`, the
            event-bus name / detail-type addressing string. The broker host or
            AWS region for queue kinds lives in the encrypted credential, not
            here.
          examples:
            - https://hooks.example.com/lerian
        event_types:
          type: array
          description: >-
            The `eventKey` values to deliver, as `GET /v1/catalog` serves them.
            A value the catalog does not list at the schema major the
            subscription receives it at, from the `origin` producer when one is
            set, is refused with `422 unknown_event_type`. When the catalog
            cannot be read or lists no producer event yet, the values are
            accepted unchecked. Omit the field to receive every event type.
          items:
            type: string
          examples:
            -   - transaction.posted
        origin:
          type: string
          description: The `ce-source` of a producing application.
          examples:
            - ledger
        timeout_ms:
          type: integer
          description: >-
            The per-attempt delivery timeout in milliseconds, from `1000` to
            `20000`. For `webhook` subscriptions only: the hub refuses a
            non-zero value for any other sink kind. Omit it, or send `0`, to
            use the `10000` default.
          examples:
            - 20000
        schema_major:
          type: integer
          description: >-
            Optional schema-major pin. Omit it to follow major 1. `event_majors`
            overrides it per event type.
          examples:
            - 1
        event_majors:
          type: object
          additionalProperties:
            type: integer
          description: >-
            Optional schema major per event type, overriding `schema_major` for
            the types it names. When `event_types` lists types, each key must be
            one of them. Each value is from `1` to `2147483647`. It cannot be
            patched: create a new subscription to change it.
        plan_tier:
          type: string
          description: The plan tier for the subscription.
          examples:
            - standard
      required:
        - name
        - sink_kind
    Subscription:
      type: object
      additionalProperties: false
      description: >-
        The non-secret projection of a subscription. It never carries the signing
        secret or any credential material.
      properties:
        id:
          type: string
          format: uuid
          description: The unique identifier of the subscription (UUIDv7).
          examples:
            - 0192f1a0-0000-7000-8000-00000000c001
        name:
          type: string
          description: The human label supplied at create.
          examples:
            - orders-webhook
        sink_kind:
          $ref: "#/components/schemas/SinkKind"
        endpoint:
          type: string
          description: The destination address (per sink kind).
          examples:
            - https://hooks.example.com/ingest
        event_types:
          type: array
          description: The event types the subscription delivers. Omitted when none are pinned.
          items:
            type: string
          examples:
            -   - account.created
                - account.updated
        schema_major:
          type: integer
          description: The pinned schema major, when set.
          examples:
            - 1
        event_majors:
          type: object
          additionalProperties:
            type: integer
          description: The schema major per event type that overrides `schema_major`. Omitted when none is set.
        origin:
          type: string
          description: The `ce-source` of a producing application. Omitted when none is set.
          examples:
            - ledger
        timeout_ms:
          type: integer
          description: The per-attempt delivery timeout in milliseconds. Omitted when every attempt uses the `10000` default.
          examples:
            - 20000
        signature_version:
          type: integer
          description: The webhook signature scheme version.
          examples:
            - 1
        plan_tier:
          type: string
          description: The subscription's plan tier.
          examples:
            - standard
        enabled:
          type: boolean
          description: >-
            Whether delivery is enabled: `false` once auto-disable trips;
            `POST /v1/subscriptions/{id}/verify` re-enables it. Independent of
            `verification_state`; both must hold for delivery.
          examples:
            - true
        verification_state:
          $ref: "#/components/schemas/VerificationState"
        created_at:
          type: string
          format: date-time
          description: Creation timestamp (UTC, RFC 3339).
          examples:
            - "2026-01-15T12:00:00Z"
        updated_at:
          type: string
          format: date-time
          description: Last-update timestamp (UTC, RFC 3339).
          examples:
            - "2026-01-15T12:00:00Z"
      required:
        - id
        - name
        - sink_kind
        - endpoint
        - signature_version
        - plan_tier
        - enabled
        - verification_state
        - created_at
        - updated_at
    SubscriptionRead:
      type: object
      additionalProperties: false
      description: >-
        The non-secret projection of a subscription on the list and detail
        routes. It carries `endpoint_display` instead of the full destination,
        and never carries the signing secret or any credential material.
      properties:
        id:
          type: string
          format: uuid
          description: The unique identifier of the subscription (UUIDv7).
          examples:
            - 0192f1a0-0000-7000-8000-00000000c001
        name:
          type: string
          description: The human label supplied at create.
          examples:
            - orders-webhook
        sink_kind:
          $ref: "#/components/schemas/SinkKind"
        endpoint_display:
          type: string
          description: >-
            A short rendering of the destination: scheme and host for a
            `webhook`; the queue, exchange, or bus name for a queue sink; the
            `pull://` value unchanged. Empty when the stored destination cannot
            be parsed. Read the full destination with
            `GET /v1/subscriptions/{id}/endpoint`.
          examples:
            - https://hooks.example.com
        event_types:
          type: array
          description: The event types the subscription delivers. Omitted when none are pinned.
          items:
            type: string
          examples:
            -   - account.created
                - account.updated
        origin:
          type: string
          description: The `ce-source` of a producing application. Omitted when none is set.
          examples:
            - ledger
        timeout_ms:
          type: integer
          description: The per-attempt delivery timeout in milliseconds. Omitted when every attempt uses the `10000` default.
          examples:
            - 20000
        schema_major:
          type: integer
          description: The pinned schema major, when set.
          examples:
            - 1
        event_majors:
          type: object
          additionalProperties:
            type: integer
          description: The schema major per event type that overrides `schema_major`. Omitted when none is set.
        signature_version:
          type: integer
          description: The webhook signature scheme version.
          examples:
            - 1
        plan_tier:
          type: string
          description: The subscription's plan tier.
          examples:
            - standard
        enabled:
          type: boolean
          description: >-
            Whether delivery is enabled: `false` once auto-disable trips;
            `POST /v1/subscriptions/{id}/verify` re-enables it. Independent of
            `verification_state`; both must hold for delivery.
          examples:
            - true
        verification_state:
          $ref: "#/components/schemas/VerificationState"
        credential_kind:
          type: string
          enum:
            - signing_secret
            - static_credential
            - delegated_grant
            - none
          description: >-
            The credential the subscription delivers with: `signing_secret` (a
            webhook signing secret), `static_credential` (a stored queue
            credential), `delegated_grant` (a cross-account AWS role; nothing is
            stored or rotated), or `none`.
          examples:
            - signing_secret
        secret_configured:
          type: boolean
          description: >-
            Whether stored secret material is present: a signing secret, or a
            queue sink's stored delivery credential. The material itself is never
            returned. Read `credential_kind` for what the subscription delivers
            with.
          examples:
            - true
        secret_rotated_at:
          type:
            - string
            - "null"
          format: date-time
          description: >-
            When the stored secret material was last replaced (UTC, RFC 3339).
            `null` when no secret is stored or the hub has no record of the time.
          examples:
            - "2026-01-15T12:00:00Z"
        secret_overlap_until:
          type:
            - string
            - "null"
          format: date-time
          description: >-
            Until when the previous signing secret stays valid after a rotation
            (UTC, RFC 3339). `null` when no overlap window is open. A second
            rotation inside an open window replaces the previous secret and
            restarts the window.
          examples:
            - "2026-01-16T12:00:00Z"
        delegated_scope:
          type: string
          description: The Base64 delegated scope of the subscription. Omitted on a tenant-wide subscription.
          examples:
            - Rk9ORE8tQQ==
        created_at:
          type: string
          format: date-time
          description: Creation timestamp (UTC, RFC 3339).
          examples:
            - "2026-01-15T12:00:00Z"
        updated_at:
          type: string
          format: date-time
          description: Last-update timestamp (UTC, RFC 3339).
          examples:
            - "2026-01-15T12:00:00Z"
      required:
        - id
        - name
        - sink_kind
        - endpoint_display
        - signature_version
        - plan_tier
        - enabled
        - verification_state
        - credential_kind
        - secret_configured
        - secret_rotated_at
        - secret_overlap_until
        - created_at
        - updated_at
    CreateSubscriptionResponse:
      type: object
      additionalProperties: false
      description: >-
        The create response. `signingSecret` carries a secret only for a
        `webhook` sink; it is stored only as ciphertext. No read route returns
        it; a replay of the same `X-Idempotency` key returns this same response.
      properties:
        subscription:
          $ref: "#/components/schemas/Subscription"
        signingSecret:
          type: string
          description: >-
            The webhook signing secret, minted server-side. No read route returns
            it; a replay of the same `X-Idempotency` key returns this same
            response. An empty string for any other sink kind.
          examples:
            - AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=
      required:
        - subscription
        - signingSecret
    ListSubscriptionsResponse:
      type: object
      additionalProperties: false
      properties:
        subscriptions:
          type: array
          items:
            $ref: "#/components/schemas/SubscriptionRead"
        next_cursor:
          type: string
          description: >-
            Keyset cursor for the next page (the id of the last row). Non-empty
            only on a full page; an empty string marks the final page.
          examples:
            - 0192f1a0-0000-7000-8000-00000000c002
      required:
        - subscriptions
        - next_cursor
    SubscriptionResponse:
      type: object
      additionalProperties: false
      properties:
        subscription:
          $ref: "#/components/schemas/SubscriptionRead"
      required:
        - subscription
    PatchSubscriptionRequest:
      type: object
      additionalProperties: false
      description: >-
        The narrow change body. `schema_major` is required: an integer `>= 1`
        re-pins; `null` clears the pin. `timeout_ms` is
        optional. Any other field or an empty body is rejected with
        `422 validation_error`.
      properties:
        schema_major:
          type:
            - integer
            - "null"
          description: The schema major to pin, or `null` to follow major 1. A value `< 1` is rejected.
          examples:
            - 1
        timeout_ms:
          type: integer
          description: >-
            The per-attempt delivery timeout in milliseconds, from `1000` to
            `20000`. For `webhook` subscriptions only: the hub refuses a
            non-zero value for any other sink kind. Omit it to keep the stored
            timeout; send `0` to return to the `10000` default.
          examples:
            - 20000
      required:
        - schema_major
    RotateSecretResponse:
      type: object
      additionalProperties: false
      properties:
        signingSecret:
          type: string
          description: The new webhook signing secret.
          examples:
            - ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8=
        overlapUntil:
          type: string
          format: date-time
          description: The moment until which each webhook delivery also carries a signature made with the previous secret (UTC, RFC 3339) — a 24-hour dual-sign overlap.
          examples:
            - "2026-06-06T12:00:00Z"
      required:
        - signingSecret
        - overlapUntil
    ProbeResult:
      type: object
      additionalProperties: false
      description: >-
        The classified result of a synchronous probe (ping, verify, or credential
        write). A classified failure is still returned with HTTP `200` — the
        probe ran and produced a verdict. It carries no secret or endpoint.
      properties:
        outcome:
          $ref: "#/components/schemas/Outcome"
        statusCode:
          type: integer
          description: >-
            The target's protocol status. For webhook probes, the HTTP status
            (`0` when none applies, e.g. a transport error or a blocked
            endpoint). For queue-kind credential probes, always `0`.
          examples:
            - 200
        errorClass:
          type: string
          description: A frozen taxonomy token classifying a failure. Empty on success.
          examples:
            - ""
        detail:
          type: string
          description: >-
            Why the probe failed: the HTTP status the endpoint answered, the
            timeout, the transport cause, the failed AWS step with its AWS code,
            or the class token. Absent on success.
          examples:
            - endpoint answered HTTP 503
      required:
        - outcome
        - statusCode
        - errorClass
    CredentialRequest:
      type: object
      additionalProperties: false
      description: The queue-credential body. `sinkConfig` is a kind-specific, write-only object that is probed and, on success, stored encrypted. It is never returned on any read path.
      properties:
        sinkConfig:
          type: object
          additionalProperties: true
          description: >-
            The candidate outbound credential for the queue kind. For `rabbitmq`,
            an object such as `{"uri":"amqps://user:pass@broker.example.com:5671/vhost"}`;
            for AWS kinds, the region and access material. Write-only — never
            logged or returned.
          examples:
            - uri: amqps://user:pass@broker.example.com:5671/vhost
      required:
        - sinkConfig
    DelegatedGrantRequest:
      type: object
      additionalProperties: false
      description: >-
        The non-secret AWS delegated-grant coordinates. No AWS credential crosses
        this body — the role is assumed per delivery, guarded by the separately
        minted `ExternalId`.
      properties:
        roleArn:
          type: string
          description: The cross-account delivery role ARN Streaming Hub assumes at delivery time.
          examples:
            - arn:aws:iam::444455556666:role/streaming-hub-delegated-delivery
        region:
          type: string
          description: The AWS region of the destination.
          examples:
            - us-east-1
        destination:
          type: string
          description: The resolved destination (queue URL or event-bus ARN). SSRF-validated before the write.
          examples:
            - https://sqs.us-east-1.amazonaws.com/444455556666/orders
      required:
        - roleArn
        - region
        - destination
    SetupArtifactsResponse:
      type: object
      additionalProperties: false
      properties:
        externalId:
          type: string
          description: The non-secret `sts:ExternalId` correlation value pinned into the trust policy (minted once, stable across re-fetches).
          examples:
            - 0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b
        region:
          type: string
          description: The AWS region the artifacts target.
          examples:
            - us-east-1
        trustPolicy:
          type: string
          description: The cross-account IAM trust policy JSON (as a string) the customer applies in their AWS account.
          examples:
            - '{ "Version": "2012-10-17", "Statement": [] }'
        quickCreateUrl:
          type: string
          format: uri
          description: A CloudFormation quick-create console link that provisions the delivery role.
          examples:
            - https://us-east-1.console.aws.amazon.com/cloudformation/home?...
      required:
        - externalId
        - region
        - trustPolicy
        - quickCreateUrl
    SubscriptionHealth:
      type: object
      additionalProperties: false
      description: The consolidated, non-secret delivery-health rollup for one subscription.
      properties:
        subscription_id:
          type: string
          format: uuid
          description: The subscription the rollup describes.
          examples:
            - 0192f1a0-0000-7000-8000-00000000a001
        enabled:
          type: boolean
          description: >-
            Whether delivery is enabled: `false` once auto-disable trips;
            `POST /v1/subscriptions/{id}/verify` re-enables it.
          examples:
            - true
        verification_state:
          $ref: "#/components/schemas/VerificationState"
        status:
          $ref: "#/components/schemas/HealthStatus"
        snapshot_at:
          type: string
          format: date-time
          description: >-
            The database instant the view was read at (UTC). Every count covers
            `[window_start, snapshot_at)`.
          examples:
            - "2026-10-17T12:00:00.123456Z"
        window_start:
          type: string
          format: date-time
          description: >-
            The start of the measured interval (UTC): `snapshot_at` minus the
            health window (30 days by default), or the start of the
            uninterrupted retained history when that is later. It equals
            `snapshot_at` when no history is retained, which is not evidence of
            successful delivery.
          examples:
            - "2026-09-17T12:00:00.123456Z"
        delivery_outcomes:
          type: object
          additionalProperties:
            type: integer
          description: >-
            Delivery-attempt outcome counts in `[window_start, snapshot_at)`
            (keyed by outcome, e.g. `delivered`, `failed`, `dead_lettered`).
            Always an object, never null.
          examples:
            - delivered: 7
              failed: 2
              dead_lettered: 1
        recent_outcomes:
          type: object
          additionalProperties:
            type: integer
          description: >-
            The same outcome histogram restricted to the last 24 hours, never
            before `window_start` (a subset of `delivery_outcomes`). Always an
            object; an outcome with no recent activity is simply a missing key.
          examples:
            - delivered: 3
              failed: 1
        success_rate:
          type:
            - number
            - "null"
          description: >-
            The derived per-attempt delivery rate over the window
            (`delivered / (delivered + failed + dead_lettered)`), in `[0.0, 1.0]`.
            `null` when there are zero terminal attempts (distinct from a genuine
            `0.0`). Per-attempt, not per-job, and informational only — it does not
            change `status`.
          examples:
            - 0.7
        dead_lettered:
          type: integer
          description: The count of dead-lettered attempts in `[window_start, snapshot_at)`.
          examples:
            - 1
        poison_events:
          type: integer
          description: >-
            A best-effort count of quarantined (poison) events in
            `[window_start, snapshot_at)`, keyed on the untrusted captured tenant
            id; drives `dropped_tenant`.
          examples:
            - 3
        dropped_tenant:
          type: boolean
          description: True when quarantined tenant events were observed.
          examples:
            - true
        consumer_lag_ms:
          type: integer
          format: int64
          description: Always `0`.
          examples:
            - 0
        auto_disabled:
          type:
            - boolean
            - "null"
          description: >-
            The auto-disable verdict: `null` when the subscription has no
            recorded attempts yet, `false` when a health row exists with no
            verdict, `true` when auto-disable has tripped (which drives
            `status: "Down"`).
          examples:
            - true
        disabled_reason:
          type: string
          description: A fixed, secret-free auto-disable verdict token (`sustained_failure_5d12h`). Omitted when empty.
          examples:
            - sustained_failure_5d12h
        last_success_at:
          type: string
          format: date-time
          description: The last successful delivery timestamp (RFC 3339). Omitted when none.
          examples:
            - "2026-06-01T12:00:00Z"
        last_failure_at:
          type: string
          format: date-time
          description: The last failed delivery timestamp (RFC 3339). Omitted when none.
          examples:
            - "2026-06-05T09:30:00Z"
      required:
        - subscription_id
        - enabled
        - verification_state
        - status
        - snapshot_at
        - window_start
        - delivery_outcomes
        - recent_outcomes
        - success_rate
        - dead_lettered
        - poison_events
        - dropped_tenant
        - consumer_lag_ms
        - auto_disabled
    CatalogResponse:
      type: object
      additionalProperties: false
      properties:
        events:
          type: array
          description: >-
            The catalog entries, ordered by topic, schema major, event type, and
            resource type. An event published at several majors appears once
            per major. Empty when the snapshot is empty.
          items:
            $ref: "#/components/schemas/CatalogEvent"
        producers:
          type: array
          description: >-
            The refresh freshness of each producer the hub tracks, ordered by
            `target`. Join an event to its producer on `source`. Always present;
            an empty list is `[]`.
          items:
            $ref: "#/components/schemas/CatalogProducer"
      required:
        - events
        - producers
    CatalogEvent:
      type: object
      additionalProperties: false
      properties:
        eventKey:
          type: string
          description: >-
            The exact value to put in a subscription's `event_types`. For a
            producer event it is `resourceType` and `eventType` joined by a dot
            (`transaction.posted`); for the hub's own `hub.*` lifecycle events
            it is the event type as is (`hub.destination.disabled`). Use it as
            served; do not build it from the other fields.
          examples:
            - transaction.posted
        source:
          type: string
          description: >-
            The producing application's `ce-source`: the value a subscription's
            `origin` names. Join it to `producers[].source` to see when the
            entry was last refreshed. `streaming-hub` is the hub's own source:
            those entries have no `producers[]` row and are always current. Any
            other source with no `producers[]` row is an entry the hub no
            longer refreshes; treat it as stale.
          examples:
            - ledger
        resourceType:
          type: string
          description: The producer's resource segment, for grouping and labels. Do not build a subscription selector from it; use `eventKey`.
          examples:
            - transaction
        eventType:
          type: string
          description: The event verb as the producer declares it. It is not unique on its own; subscribe with `eventKey`.
          examples:
            - posted
        topic:
          type: string
          description: >-
            The Kafka topic the event is published on. Each producing
            application has one topic, so every event of a producer repeats it.
          examples:
            - lerian.streaming.ledger
        schemaVersion:
          type: string
          description: >-
            The producer-declared schema version. Within one resource type,
            event type, and schema major, the catalog serves the highest minor
            version the manifest declares.
          examples:
            - 1.0.0
        schemaMajor:
          type: integer
          description: The schema major.
          examples:
            - 1
        dataSchema:
          type: string
          description: An opaque schema URL passthrough. Omitted when empty; Streaming Hub never resolves or fetches it.
          examples:
            - https://schemas.example.com/transaction/posted/1.json
        description:
          type: string
          description: A human-readable description of the event.
          examples:
            - A transaction was posted.
      required:
        - eventKey
        - source
        - resourceType
        - eventType
        - topic
        - schemaVersion
        - schemaMajor
        - description
    CatalogProducer:
      type: object
      additionalProperties: false
      description: The refresh freshness of one producer the hub tracks.
      properties:
        target:
          type: string
          description: >-
            The hub's identity for this producer's refresh. In a single-tenant
            deployment it is the host of the configured manifest URL, with the
            port when it is not the default (`ledger.internal:8080`). In a
            multi-tenant deployment it is the service label (`ledger`). It is
            not a `ce-source`: never match it against one.
          examples:
            - ledger.internal
        source:
          type:
            - string
            - "null"
          description: >-
            The producer's own `ce-source`, read from its manifest. It joins to
            `events[].source` one to one. `null` until the first successful
            refresh.
          examples:
            - ledger
        fetched_at:
          type:
            - string
            - "null"
          format: date-time
          description: >-
            When the last successful refresh of this producer's entries ran (UTC,
            RFC 3339). `null` when no refresh has succeeded yet. A failed or
            skipped attempt does not move it.
          examples:
            - "2026-06-02T09:00:00Z"
        last_refresh_failed_at:
          type:
            - string
            - "null"
          format: date-time
          description: >-
            When the last failed or skipped refresh attempt ran (UTC, RFC 3339).
            `null` when no attempt has failed. A later success does not clear
            it: compare it with `fetched_at` to tell a recovered producer from a
            broken one.
          examples:
            - "2026-06-04T09:00:00Z"
        last_refresh_error_class:
          type:
            - string
            - "null"
          enum:
            - fetch_failed
            - invalid_manifest
            - persist_failed
            - breaker_open
            - null
          description: >-
            The failure class of `last_refresh_failed_at`; `null` exactly when
            that field is `null`. `fetch_failed`: the producer was unreachable,
            timed out, answered with a non-2xx status, or served an unparseable
            body. `invalid_manifest`: the manifest parsed but broke the manifest
            contract. `persist_failed`: the hub could not store a valid
            manifest; the fault is not the producer's. `breaker_open`: the
            producer's circuit breaker was open, so no fetch ran. It never
            carries the raw error text.
          examples:
            - fetch_failed
      required:
        - target
        - source
        - fetched_at
        - last_refresh_failed_at
        - last_refresh_error_class
    PullEventsResponse:
      type: object
      additionalProperties: false
      properties:
        events:
          type: array
          description: A page of events in ascending `seq` order.
          items:
            $ref: "#/components/schemas/PullEvent"
        next_cursor:
          type:
            - integer
            - "null"
          format: int64
          description: >-
            The maximum `seq` in the page when it filled exactly to `limit` (a
            possible next page); `null` on a short or final page. This diverges
            deliberately from the subscriptions list cursor (which is a
            DESC-by-id string with an empty-string sentinel).
          examples:
            - 42
      required:
        - events
        - next_cursor
    PullEvent:
      type: object
      additionalProperties: false
      properties:
        seq:
          type: integer
          format: int64
          description: >-
            The tenant arrival-order sequence number (the keyset key). It ascends
            within the subscription's stream with gaps: the numbers between
            belong to the tenant's events this subscription does not receive.
          examples:
            - 42
        ceId:
          type: string
          description: The CloudEvents id (UUIDv7) — the stable dedup key. Deduplicate on this across at-least-once redeliveries.
          examples:
            - 0192f1a0-0000-7000-8000-0000000000ce
        eventType:
          type: string
          description: The event type (the `<resource>.<event>` tail).
          examples:
            - orders.created
        schemaVersion:
          type: string
          description: The event's schema version.
          examples:
            - 1.0.0
        receivedAt:
          type: string
          format: date-time
          description: When the hub received the event (UTC, RFC 3339).
          examples:
            - "2026-06-06T12:00:00Z"
        payload:
          type: object
          additionalProperties: true
          description: The producer's event payload, surfaced opaquely (arbitrary JSON). Carries no secret material.
          examples:
            - order_id: abc
              amount: 42
      required:
        - seq
        - ceId
        - eventType
        - schemaVersion
        - receivedAt
        - payload
    DLQResponse:
      type: object
      additionalProperties: false
      properties:
        observations:
          type: array
          description: A page of dead-letter observations, reverse-chronological.
          items:
            $ref: "#/components/schemas/DLQObservation"
        nextCursor:
          type: string
          description: Keyset cursor for the next (older) page — the id of the last row. Empty when there are no further pages.
          examples:
            - 0192f1a0-0000-7000-8000-00000000d017
      required:
        - observations
        - nextCursor
    DLQObservation:
      type: object
      additionalProperties: false
      description: >-
        One dead-letter observation. Carries no secret, no customer endpoint URL,
        and no signing material. `tenant_id_raw` is an untrusted, best-effort
        capture surfaced for forensic value only.
      properties:
        source_topic:
          type: string
          description: The source topic the failed delivery originated from.
          examples:
            - lerian.streaming.midaz
        error_class:
          type: string
          description: The transport error class captured for the failed delivery.
          examples:
            - broker_unavailable
        error_message:
          type: string
          description: A sanitized error message (redacted at capture; never a secret or host:port).
          examples:
            - connection refused
        retry_count:
          type: integer
          description: The number of delivery attempts recorded before dead-lettering.
          examples:
            - 8
        first_failure_at:
          type: string
          format: date-time
          description: When the first failure was observed (UTC, RFC 3339).
          examples:
            - "2026-06-05T09:00:00Z"
        producer_id:
          type: string
          description: The producer identity captured for the source event.
          examples:
            - midaz
        observed_at:
          type: string
          format: date-time
          description: When the dead-letter observation was recorded (UTC, RFC 3339).
          examples:
            - "2026-06-05T09:30:00Z"
        tenant_id_raw:
          type: string
          description: The untrusted captured tenant id. Forensic only — never an authorization or scoping input.
          examples:
            - tenant-acme
      required:
        - source_topic
        - error_class
        - error_message
        - retry_count
        - first_failure_at
        - producer_id
        - observed_at
        - tenant_id_raw
    EndpointResponse:
      type: object
      additionalProperties: false
      properties:
        endpoint:
          type: string
          description: >-
            The full delivery destination as stored: the webhook URL with its
            path and query, the queue URL with its account id, the exchange and
            routing key, the bus name, or the `pull://` value.
          examples:
            - https://hooks.example.com/ingest
      required:
        - endpoint
    DashboardSnapshot:
      type: object
      additionalProperties: false
      properties:
        snapshot_at:
          type: string
          format: date-time
          description: >-
            The database instant the read observed (UTC, RFC 3339). Every
            `current_state` figure is true at this instant, and `window_end` is
            never after it.
          examples:
            - "2026-09-11T12:00:00Z"
        coverage_start:
          type: string
          format: date-time
          description: >-
            The earliest instant of the uninterrupted retained history this read
            could count (UTC, RFC 3339). A period that starts before it is refused
            with `422 validation_error`.
          examples:
            - "2026-09-04T00:00:00Z"
        window_start:
          type: string
          format: date-time
          description: The inclusive start of the resolved period (UTC, RFC 3339). Every `selected_period` count is taken over `[window_start, window_end)`.
          examples:
            - "2026-09-10T12:00:00Z"
        window_end:
          type: string
          format: date-time
          description: The exclusive end of the resolved period (UTC, RFC 3339). It is `snapshot_at` when the request omits `to`.
          examples:
            - "2026-09-11T12:00:00Z"
        current_state:
          $ref: "#/components/schemas/DashboardCurrentState"
        selected_period:
          $ref: "#/components/schemas/DashboardSelectedPeriod"
      required:
        - snapshot_at
        - coverage_start
        - window_start
        - window_end
        - current_state
        - selected_period
    DashboardCurrentState:
      type: object
      additionalProperties: false
      description: What the tenant's hub looks like at `snapshot_at`, independent of the period.
      properties:
        subscriptions:
          $ref: "#/components/schemas/DashboardSubscriptionCensus"
        push:
          $ref: "#/components/schemas/DashboardCurrentPush"
      required:
        - subscriptions
        - push
    DashboardSubscriptionCensus:
      type: object
      additionalProperties: false
      description: >-
        The subscription census at `snapshot_at`: a total and four independent
        splits of the same subscriptions. Each split sums to `total`. Every
        count is `0` when none, never `null`.
      properties:
        total:
          type: integer
          description: The subscriptions in scope.
          examples:
            - 12
        by_enabled:
          $ref: "#/components/schemas/DashboardEnabledCounts"
        by_verification_state:
          $ref: "#/components/schemas/DashboardVerificationStateCounts"
        by_sink_kind:
          $ref: "#/components/schemas/DashboardSinkKindCounts"
        by_channel:
          $ref: "#/components/schemas/DashboardChannelCounts"
      required:
        - total
        - by_enabled
        - by_verification_state
        - by_sink_kind
        - by_channel
    DashboardEnabledCounts:
      type: object
      additionalProperties: false
      description: The census split by the `enabled` flag.
      properties:
        enabled:
          type: integer
          examples:
            - 10
        disabled:
          type: integer
          examples:
            - 2
      required:
        - enabled
        - disabled
    DashboardVerificationStateCounts:
      type: object
      additionalProperties: false
      description: >-
        The census split by verification state. `disabled` is a reserved state
        that no operation sets.
      properties:
        pending_verification:
          type: integer
          examples:
            - 1
        active:
          type: integer
          examples:
            - 9
        degraded:
          type: integer
          examples:
            - 2
        disabled:
          type: integer
          examples:
            - 0
      required:
        - pending_verification
        - active
        - degraded
        - disabled
    DashboardSinkKindCounts:
      type: object
      additionalProperties: false
      description: The census split by sink kind.
      properties:
        webhook:
          type: integer
          examples:
            - 7
        sqs:
          type: integer
          examples:
            - 2
        rabbitmq:
          type: integer
          examples:
            - 1
        eventbridge:
          type: integer
          examples:
            - 1
        pull:
          type: integer
          examples:
            - 1
      required:
        - webhook
        - sqs
        - rabbitmq
        - eventbridge
        - pull
    DashboardChannelCounts:
      type: object
      additionalProperties: false
      description: The census split by delivery channel. `pull` counts the `pull` sink kind; `push` counts every other kind.
      properties:
        push:
          type: integer
          examples:
            - 11
        pull:
          type: integer
          examples:
            - 1
      required:
        - push
        - pull
    DashboardCurrentPush:
      type: object
      additionalProperties: false
      description: The push delivery backlog at `snapshot_at`.
      properties:
        pending_jobs:
          type: integer
          description: The delivery jobs still waiting for work (pending or retrying). `0` when none.
          examples:
            - 3
        oldest_pending_job_age_seconds:
          type:
            - integer
            - "null"
          description: The age of the oldest pending job at `snapshot_at`, in whole seconds. `null` exactly when `pending_jobs` is `0`.
          examples:
            - 42
      required:
        - pending_jobs
        - oldest_pending_job_age_seconds
    DashboardSelectedPeriod:
      type: object
      additionalProperties: false
      description: The counts over `[window_start, window_end)`. Every count is `0` when none, never `null`.
      properties:
        events_received:
          type: integer
          description: >-
            The events the hub received in the period. Not a delivery count: a
            received event can match no subscription.
          examples:
            - 1280
        push:
          $ref: "#/components/schemas/DashboardPushPeriod"
        pull:
          $ref: "#/components/schemas/DashboardPullPeriod"
        pull_omitted_reason:
          type:
            - string
            - "null"
          description: >-
            Why `pull` was not computed. `null` exactly when `pull` is present.
            The only value is `window_exceeds_pull_ceiling`.
          examples:
            - window_exceeds_pull_ceiling
        pull_window_ceiling_hours:
          type: integer
          description: >-
            The widest period, in whole hours, for which the hub computes
            `pull`. A period of exactly this width still carries `pull`.
          examples:
            - 48
        time_series:
          type: array
          description: >-
            Contiguous ascending buckets that tile the period with no gap or
            overlap: hourly (UTC) for a period up to 48 hours, daily (UTC) above
            it. The bucket counts sum to the period counts, except
            `pull.eligible_events`, which is a distinct count taken separately
            in each bucket and can sum higher. An empty series is `[]`.
          items:
            $ref: "#/components/schemas/DashboardTimeBucket"
      required:
        - events_received
        - push
        - pull
        - pull_omitted_reason
        - pull_window_ceiling_hours
        - time_series
    DashboardPushPeriod:
      type: object
      additionalProperties: false
      description: The push side of a period or bucket.
      properties:
        delivery_jobs_created:
          type: integer
          description: The delivery jobs created in the period. A job created here can have no attempt here.
          examples:
            - 640
        delivery_attempts:
          $ref: "#/components/schemas/DashboardDeliveryAttemptCounts"
      required:
        - delivery_jobs_created
        - delivery_attempts
    DashboardDeliveryAttemptCounts:
      type: object
      additionalProperties: false
      description: The delivery attempts made in the period, by outcome. Counts are per attempt, not per job.
      properties:
        delivered:
          type: integer
          examples:
            - 600
        failed:
          type: integer
          examples:
            - 30
        dead_lettered:
          type: integer
          examples:
            - 6
        disabled:
          type: integer
          examples:
            - 2
        available:
          type: integer
          examples:
            - 1
        poison:
          type: integer
          examples:
            - 1
      required:
        - delivered
        - failed
        - dead_lettered
        - disabled
        - available
        - poison
    DashboardPullPeriod:
      type:
        - object
        - "null"
      additionalProperties: false
      description: >-
        Pull eligibility in the period, never delivery: a pull consumer
        acknowledges by moving its own cursor. It is computed from the pull
        subscriptions that exist now, so deleting or disabling one changes the
        counts of a closed period. `null` when the period exceeds
        `pull_window_ceiling_hours`.
      properties:
        eligible_events:
          type: integer
          description: The distinct events in the period that match at least one pull subscription.
          examples:
            - 420
        eligible_subscription_matches:
          type: integer
          description: The event and pull-subscription pairs in the period. At least `eligible_events` when an event matches more than one subscription.
          examples:
            - 440
      required:
        - eligible_events
        - eligible_subscription_matches
    DashboardTimeBucket:
      type: object
      additionalProperties: false
      properties:
        bucket_start:
          type: string
          format: date-time
          description: The inclusive bucket start (UTC), clipped to `window_start` on the first bucket.
          examples:
            - "2026-09-10T12:00:00Z"
        bucket_end:
          type: string
          format: date-time
          description: The exclusive bucket end (UTC), clipped to `window_end` on the last bucket.
          examples:
            - "2026-09-10T13:00:00Z"
        events_received:
          type: integer
          description: The events received in this bucket. A bucket with no events carries `0`.
          examples:
            - 53
        push:
          $ref: "#/components/schemas/DashboardPushPeriod"
        pull:
          $ref: "#/components/schemas/DashboardPullPeriod"
      required:
        - bucket_start
        - bucket_end
        - events_received
        - push
        - pull
    NeedsAttentionPage:
      type: object
      additionalProperties: false
      properties:
        snapshot_at:
          type: string
          format: date-time
          description: The database instant the read observed (UTC, RFC 3339).
          examples:
            - "2026-09-11T12:00:00Z"
        window_start:
          type: string
          format: date-time
          description: The inclusive start of the resolved period (UTC, RFC 3339). Repeat it as `from` on the next page.
          examples:
            - "2026-09-10T12:00:00Z"
        window_end:
          type: string
          format: date-time
          description: The exclusive end of the resolved period (UTC, RFC 3339). Repeat it as `to` on the next page.
          examples:
            - "2026-09-11T12:00:00Z"
        items:
          type: array
          description: >-
            The page, ordered by attention rank, then `last_failure_at` (most
            recent first, never-failed last), then `subscription_id`. An empty
            page is `[]`.
          items:
            $ref: "#/components/schemas/NeedsAttentionItem"
        next_cursor:
          type: string
          description: The signed resume token for the next page. An empty string on the last page.
          examples:
            - ""
      required:
        - snapshot_at
        - window_start
        - window_end
        - items
        - next_cursor
    NeedsAttentionItem:
      type: object
      additionalProperties: false
      description: One subscription that needs attention. Every period count is `0` when none, never `null`.
      properties:
        subscription_id:
          type: string
          format: uuid
          description: The subscription that needs attention.
          examples:
            - 0192f1a0-0000-7000-8000-0000000000ff
        sink_kind:
          $ref: "#/components/schemas/SinkKind"
        enabled:
          type: boolean
          description: >-
            Whether delivery is enabled: `false` once auto-disable trips;
            `POST /v1/subscriptions/{id}/verify` re-enables it.
          examples:
            - false
        verification_state:
          $ref: "#/components/schemas/VerificationState"
        auto_disabled:
          type: boolean
          description: Whether the hub's auto-disable tripped for this destination.
          examples:
            - true
        disabled_reason:
          type: string
          description: The fixed, secret-free auto-disable verdict token. An empty string when no verdict was recorded.
          examples:
            - sustained_failure_5d12h
        last_success_at:
          type:
            - string
            - "null"
          format: date-time
          description: The last successful delivery (UTC, RFC 3339). `null` when none.
          examples:
            - "2026-09-10T09:30:00Z"
        last_failure_at:
          type:
            - string
            - "null"
          format: date-time
          description: The last failed delivery (UTC, RFC 3339). `null` when none.
          examples:
            - "2026-09-11T09:29:00Z"
        period_delivery_attempts:
          type: integer
          description: The delivery attempts in the period.
          examples:
            - 12
        period_failed_attempts:
          type: integer
          description: The failed attempts in the period.
          examples:
            - 12
        period_dead_lettered_attempts:
          type: integer
          description: The dead-lettered attempts in the period.
          examples:
            - 3
        attention_reasons:
          type: array
          description: >-
            The reasons, in severity order: `auto_disabled`,
            `verification_not_active`, `manually_disabled`,
            `dead_lettered_in_period`,
            `failed_in_period_without_success_after_failure`. The first reason
            present sets the item's rank.
          items:
            type: string
            enum:
              - auto_disabled
              - verification_not_active
              - manually_disabled
              - dead_lettered_in_period
              - failed_in_period_without_success_after_failure
          examples:
            -   - auto_disabled
                - dead_lettered_in_period
      required:
        - subscription_id
        - sink_kind
        - enabled
        - verification_state
        - auto_disabled
        - disabled_reason
        - last_success_at
        - last_failure_at
        - period_delivery_attempts
        - period_failed_attempts
        - period_dead_lettered_attempts
        - attention_reasons
    LivenessResponse:
      type: object
      additionalProperties: false
      properties:
        status:
          type: string
          description: Always `ok` when the process is serving.
          examples:
            - ok
      required:
        - status
    ReadinessResponse:
      type: object
      description: The readiness verdict and its per-probe detail.
      properties:
        state:
          type: string
          description: The folded readiness state. `healthy` / `degraded` return `200`; `down` returns `503`.
          enum:
            - healthy
            - degraded
            - down
          examples:
            - healthy
      required:
        - state
    VersionResponse:
      type: object
      additionalProperties: false
      properties:
        version:
          type: string
          description: The build version (link-time injected).
          examples:
            - 1.0.0
        commit:
          type: string
          description: The source commit the binary was built from.
          examples:
            - a0fa64d
        buildDate:
          type: string
          description: The build timestamp.
          examples:
            - "2026-07-20T10:00:00Z"
      required:
        - version
        - commit
        - buildDate
    RuntimeResponse:
      type: object
      additionalProperties: false
      properties:
        goVersion:
          type: string
          description: The Go runtime version.
          examples:
            - go1.26.5
        goroutines:
          type: integer
          description: The current goroutine count.
          examples:
            - 42
        numGC:
          type: integer
          format: int64
          description: The number of completed GC cycles.
          examples:
            - 17
        heapAlloc:
          type: integer
          format: int64
          description: Heap bytes currently allocated.
          examples:
            - 8388608
        numCPU:
          type: integer
          description: The number of logical CPUs available.
          examples:
            - 8
      required:
        - goVersion
        - goroutines
        - numGC
        - heapAlloc
        - numCPU
