Ctrl K
ComunidadGuías…

api/openapi.yaml

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
components:
  schemas:
    AIDraftingStruct:
      additionalProperties: false
      properties:
        enabled:
          description: True when the server is started with SPARROW_AI_ENABLED=true, whether or not the provider settings are valid. Drafting (POST /v1/subscriptions:draftTemplate) is usable only when enabled is true and problem is absent.
          type: boolean
        model:
          description: Model id used for drafting; absent when drafting is off or not usable.
          type: string
        problem:
          description: "Why drafting is enabled but not usable: a missing or invalid provider setting. POST /v1/subscriptions:draftTemplate answers 503 with this text."
          type: string
        prompt_only:
          description: "Always the opposite of enabled: true when SPARROW_AI_ENABLED is not true, so clients offer only the copy-and-paste prompt. POST /v1/subscriptions:draftTemplatePrompt works whatever this says."
          type: boolean
        provider:
          description: "Chat API behind drafting: anthropic, or openai for any OpenAI-compatible server (Ollama, vLLM, OpenRouter, OpenAI). Absent when drafting is off or not usable."
          enum:
            - anthropic
            - openai
          type: string
      required:
        - enabled
        - prompt_only
      type: object
    AlertConfigItem:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/AlertConfigItem.json
          format: uri
          readOnly: true
          type: string
        consumer:
          description: Consumer this alert config belongs to.
          type: string
        created_at:
          description: Creation timestamp, RFC3339.
          format: date-time
          type: string
        email:
          description: Recipient email address.
          type: string
        event_types:
          description: Sparrow system event types this recipient is alerted on.
          items:
            type: string
          type: array
        id:
          description: Alert config id (UUID).
          type: string
        webhook_id:
          description: Webhook this alert is scoped to; omitted for consumer-wide alerts.
          type: string
      required:
        - id
        - consumer
        - email
        - event_types
        - created_at
      type: object
    AlertDeliveryStruct:
      additionalProperties: false
      properties:
        configured:
          description: True when an active webhook under the _sparrow consumer has an unpaused subscription to Sparrow's system events, so alert configs actually send email. When false, alert configs are stored but nothing is sent.
          type: boolean
      required:
        - configured
      type: object
    AttemptItem:
      additionalProperties: false
      properties:
        error_category:
          description: Failure classification for this attempt.
          enum:
            - success
            - client_error
            - server_error
            - timeout
            - dns_error
            - tls_error
            - connection_refused
            - network_error
            - rate_limited
            - unexpected_status
            - template_error
            - unknown
          type: string
        error_message:
          description: Human-readable failure reason for this attempt.
          type: string
        response_code:
          description: HTTP status code returned by the endpoint on this attempt; 0 when no response was received.
          format: int64
          type: integer
        response_time:
          description: Round-trip time of this attempt, in milliseconds.
          format: int64
          type: integer
        success:
          description: Whether this attempt succeeded (got one of the webhook's expected_status_codes).
          type: boolean
        timestamp:
          description: When this attempt was made, RFC3339.
          format: date-time
          type: string
      required:
        - success
        - response_time
        - response_code
        - timestamp
      type: object
    AttemptsOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/AttemptsOutputBody.json
          format: uri
          readOnly: true
          type: string
        items:
          description: Every recorded attempt, oldest first.
          items:
            $ref: "#/components/schemas/AttemptItem"
          type: array
      required:
        - items
      type: object
    BatchJobOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/BatchJobOutputBody.json
          format: uri
          readOnly: true
          type: string
        consumer:
          description: Consumer the snapshot was taken in; empty for a snapshot taken from a cross-consumer list.
          type: string
        created_at:
          description: Creation timestamp, RFC3339.
          format: date-time
          type: string
        expires_at:
          description: "When the snapshot expires, RFC3339: 15 minutes after it was taken. An expired snapshot can no longer be started."
          format: date-time
          type: string
        failed:
          description: Items that could not be re-pushed or retried (e.g. the event or delivery no longer exists).
          format: int64
          type: integer
        id:
          description: "Batch job id (UUID): the repush_id or retry_id from the list call."
          type: string
        job_type:
          description: Which batch operation this job performs.
          enum:
            - event_repush
            - delivery_retry
          type: string
        processed:
          description: Items handled successfully so far.
          format: int64
          type: integer
        status:
          description: "pending: snapshot taken, not started. processing: running. completed: every item was handled (see failed for the ones that could not be). failed: every item failed. cancelled: stopped by a cancel call."
          enum:
            - pending
            - processing
            - completed
            - failed
            - cancelled
          type: string
        total:
          description: Total items in the batch snapshot.
          format: int64
          type: integer
      required:
        - id
        - consumer
        - job_type
        - status
        - total
        - processed
        - failed
        - created_at
        - expires_at
      type: object
    BundleItem:
      additionalProperties: false
      properties:
        active:
          description: Whether events of this type can be pushed. Omitted means true.
          type: boolean
        description:
          description: "Human-readable summary. Omitted means empty: an import replaces the definition."
          type: string
        event_schema:
          additionalProperties: {}
          description: JSON Schema. Omitted means no schema, which removes an existing schema (a breaking change).
          type: object
        metadata:
          additionalProperties:
            type: string
          description: Arbitrary key/value metadata. Omitted means none.
          type: object
        name:
          description: Event type name.
          maxLength: 255
          minLength: 1
          type: string
      required:
        - name
      type: object
    BundleStampBody:
      additionalProperties: false
      properties:
        format:
          description: Bundle format number.
          format: int64
          type: integer
        sha256:
          description: sha256 of the canonical encoding of items, to tell whether they changed after export.
          type: string
        sparrow_version:
          description: Version of the Sparrow server that exported the bundle.
          type: string
      required:
        - sparrow_version
        - format
        - sha256
      type: object
    CapabilitiesOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/CapabilitiesOutputBody.json
          format: uri
          readOnly: true
          type: string
        ai_drafting:
          $ref: "#/components/schemas/AIDraftingStruct"
          description: AI-assisted transform template drafting.
        alert_delivery:
          $ref: "#/components/schemas/AlertDeliveryStruct"
          description: Delivery of Sparrow's own alert emails (see the alert-configs endpoints).
        portal:
          $ref: "#/components/schemas/PortalStruct"
          description: What the consumer portal is allowed to do on this server.
      required:
        - ai_drafting
        - alert_delivery
        - portal
      type: object
    ConsumerHint:
      additionalProperties: false
      properties:
        name:
          description: Consumer to register the recipe under, e.g. _sparrow.
          type: string
        note:
          description: Why, and what the recipe does under any other consumer.
          type: string
      required:
        - name
        - note
      type: object
    ConsumerItem:
      additionalProperties: false
      properties:
        name:
          description: Consumer name.
          type: string
      required:
        - name
      type: object
    ConsumerStatsOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ConsumerStatsOutputBody.json
          format: uri
          readOnly: true
          type: string
        active_webhooks:
          description: Webhooks currently active (not paused).
          format: int64
          type: integer
        failed_deliveries:
          description: Attempts that failed, all time, including failures that were later retried successfully. Lags like total_deliveries.
          format: int64
          type: integer
        pending_deliveries:
          description: Deliveries waiting to be sent or retried right now (pending, sending or retrying).
          format: int64
          type: integer
        success_rate:
          description: successful_deliveries / total_deliveries, 0.0 to 1.0.
          format: double
          type: number
        successful_deliveries:
          description: Attempts that succeeded, all time. Lags like total_deliveries.
          format: int64
          type: integer
        total_deliveries:
          description: Delivery attempts recorded, all time. A retried delivery counts once per attempt. Updated by each health evaluation (SPARROW_HEALTH_EVAL_INTERVAL), so it can lag by that much.
          format: int64
          type: integer
        total_webhooks:
          description: Total webhooks registered.
          format: int64
          type: integer
      required:
        - total_webhooks
        - active_webhooks
        - total_deliveries
        - successful_deliveries
        - failed_deliveries
        - pending_deliveries
        - success_rate
      type: object
    CreateAlertConfigBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/CreateAlertConfigBody.json
          format: uri
          readOnly: true
          type: string
        email:
          description: Recipient email address.
          format: email
          type: string
        event_types:
          description: "Sparrow system event types to alert on, at least one of: sparrow.webhook.health_changed, sparrow.webhook.delivery_failed, sparrow.webhook.disabled."
          items:
            type: string
          type: array
        webhook_id:
          description: Scope the alert to one webhook. Omit for a consumer-wide alert covering every webhook.
          type: string
      required:
        - email
        - event_types
      type: object
    CreateInviteBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/CreateInviteBody.json
          format: uri
          readOnly: true
          type: string
        consumer:
          description: "Invite into one consumer's portal: the token it creates is limited to that consumer. Omit for tenant-wide (operator console) access. Same naming rules as on POST /v1/tokens."
          type: string
        name:
          description: Name for the token the invite creates, e.g. the invitee's name.
          maxLength: 200
          minLength: 1
          type: string
        token_never_expires:
          description: The token it creates never expires; same rules as never_expires on POST /v1/tokens.
          type: boolean
        token_ttl_seconds:
          description: Lifetime in seconds of the token it creates, counted from redemption; same defaults and limits as ttl_seconds on POST /v1/tokens.
          format: int64
          minimum: 0
          type: integer
        ttl_seconds:
          description: How long the invite can be redeemed, in seconds. 0 or omitted means 24 hours; at most 7 days.
          format: int64
          minimum: 0
          type: integer
      required:
        - name
      type: object
    CreateInviteOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/CreateInviteOutputBody.json
          format: uri
          readOnly: true
          type: string
        invite:
          $ref: "#/components/schemas/InviteOut"
          description: The created invite.
        path:
          description: "UI-relative link with the secret in the fragment (never sent to servers or logs): /#invite=... for a tenant-wide invite, /portal#invite=... for a consumer invite. Prepend the base URL the UI is served from."
          type: string
        secret:
          description: The invite secret. Shown only in this response.
          type: string
      required:
        - invite
        - secret
        - path
      type: object
    CreateSubscriptionBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/CreateSubscriptionBody.json
          format: uri
          readOnly: true
          type: string
        event_name:
          description: Event type name to subscribe to, or "*" to receive every event in the consumer (catch-all). sparrow.* names only under the _sparrow consumer. One subscription per webhook, event name and consumer (409 on a duplicate).
          type: string
        headers:
          additionalProperties:
            type: string
          description: Extra HTTP headers sent with deliveries from this subscription. They override the webhook's headers of the same name; the webhook's secret_headers override both. Same name and value rules as webhook headers.
          type: object
        label_filters:
          additionalProperties:
            type: string
          description: Key/value pairs that must ALL be present in an event's labels for this subscription to receive it. Empty means match every event of event_name. Same limits as event labels (20 pairs, 64-character keys, 256-character values).
          type: object
        method:
          description: HTTP method used for deliveries from this subscription, e.g. PUT. Defaults to POST.
          type: string
        on_transform_error:
          description: "What happens when transform_template fails to render. fail (default): the delivery fails with error category template_error, is not retried automatically, and can be retried once the template is fixed; it does not affect the webhook's health. fallback: the default envelope payload is sent instead, and the error is still recorded on the delivery."
          enum:
            - fail
            - fallback
          type: string
        template_missing_key:
          description: "How transform_template reads a key the payload does not have. error (default): the render fails, so a field removed from the event schema cannot silently turn into \"<no value>\" in the body; read optional fields with index or dig (default only fills a key that is present but empty). zero: the key renders as \"<no value>\"."
          enum:
            - error
            - zero
          type: string
        timeout:
          description: Request timeout for this subscription's deliveries, in seconds, overriding the webhook's request_timeout_seconds. 0 to 300; 0 (default) uses the webhook's.
          format: int64
          type: integer
        transform_enabled:
          description: Whether to render transform_template into the delivered body instead of sending the default envelope. Defaults to false.
          type: boolean
        transform_template:
          description: Go text/template rendered against the event to produce the delivered body, used when transform_enabled is true. See GET /v1/template-functions for available helpers; test it with POST /v1/subscriptions:testTemplate. A template that does not parse is rejected with 400; it is not rendered on save. Saving a new template records a version in the template history.
          type: string
        webhook_id:
          description: Webhook id (UUID) to deliver matching events to. Must belong to this consumer.
          type: string
      required:
        - webhook_id
        - event_name
      type: object
    CreateTokenBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/CreateTokenBody.json
          format: uri
          readOnly: true
          type: string
        consumer:
          description: "Limit the token to one consumer: it then works only through the portal API (/portal/api/). Omit for a tenant-wide token with the same power as SPARROW_API_KEY. At most 255 characters, no /, \\, ?, #, % or dot segments; _sparrow is not allowed."
          type: string
        external_id:
          description: "Consumer tokens only. Your id for who or what the token is for, e.g. your user id when embedding the portal. There is at most one active token per consumer and external_id: while it is valid, asking again returns it, secret included (reused: true; name and ttl_seconds are ignored), instead of creating another; once it expires or is revoked, a new one is created. This makes minting a portal link on every page view safe. Not a secret: it is fine to log."
          maxLength: 200
          type: string
        name:
          description: Who or what the token is for, e.g. "alice" or "ci-deploy".
          maxLength: 200
          minLength: 1
          type: string
        never_expires:
          description: Create a tenant-wide token that never expires (revoke it when no longer needed). Not allowed with ttl_seconds or for consumer tokens.
          type: boolean
        ttl_seconds:
          description: "Lifetime in seconds. 0 or omitted takes the default: SPARROW_TOKEN_DEFAULT_TTL (90 days unless changed) for tenant-wide tokens, 7 days for consumer tokens. Consumer tokens allow at most 30 days."
          format: int64
          minimum: 0
          type: integer
      required:
        - name
      type: object
    CreateTokenOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/CreateTokenOutputBody.json
          format: uri
          readOnly: true
          type: string
        portal_path:
          description: "Consumer tokens only: server-relative portal URL with the token in the fragment (never sent to the server or logged). Prepend the base URL the UI is served from (the Sparrow server with SPARROW_SERVE_UI=true, or your separately hosted UI) and hand it to the end consumer."
          type: string
        reused:
          description: True when external_id matched a still-valid token, which is returned instead of a new one.
          type: boolean
        secret:
          description: "The credential. Shown only in this response (and again for the same external_id); send it as X-API-Key or Authorization: Bearer."
          type: string
        token:
          $ref: "#/components/schemas/TokenOut"
          description: The created (or reused) token.
      required:
        - token
        - secret
        - reused
      type: object
    CrlOut:
      additionalProperties: false
      properties:
        entries:
          description: Revoked certificates it lists.
          format: int64
          type: integer
        error:
          description: Why the last download or check failed. In soft mode such a CRL gives no answer and connections are allowed.
          type: string
        fetched_at:
          description: When this instance last downloaded it successfully.
          format: date-time
          type: string
        issuer:
          description: Common name of the CA that signs this CRL.
          type: string
        next_update:
          description: When the CA promises a newer CRL. Past it, the CRL is downloaded again before use.
          format: date-time
          type: string
        url:
          description: CRL distribution point, from an intermediate in a receiver's chain.
          type: string
      required:
        - url
        - issuer
        - entries
      type: object
    CursorPaginationOutput:
      additionalProperties: false
      properties:
        has_more:
          description: True when older items follow this page.
          type: boolean
        limit:
          description: Page size applied (the requested limit, default 50).
          format: int32
          type: integer
        next_cursor:
          description: Pass as cursor to get the next, older page. Absent on the last page.
          type: string
      required:
        - limit
        - has_more
      type: object
    DeliveryItem:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/DeliveryItem.json
          format: uri
          readOnly: true
          type: string
        attempt_count:
          description: Number of delivery attempts made so far, including earlier rounds before a manual retry.
          format: int64
          type: integer
        consumer:
          description: Consumer of the webhook. Set in delivery listings.
          type: string
        created_at:
          description: When the delivery was created by fan-out, RFC3339.
          format: date-time
          type: string
        delivery_id:
          description: Delivery id (UUID).
          type: string
        error_category:
          description: Outcome classification of the most recent attempt, used to decide retryability. server_error (5xx), timeout, connection_refused, network_error and rate_limited (429) are retried; client_error (other 4xx), dns_error, tls_error and unexpected_status (a 2xx/3xx not in expected_status_codes) are not. template_error means the subscription's transform failed to render; it is not retried automatically and does not count against the webhook's health.
          enum:
            - success
            - client_error
            - server_error
            - timeout
            - dns_error
            - tls_error
            - connection_refused
            - network_error
            - rate_limited
            - unexpected_status
            - template_error
            - unknown
          type: string
        error_message:
          description: Human-readable failure reason from the most recent attempt.
          type: string
        event_id:
          description: Pushed event occurrence this delivery originated from.
          type: string
        event_name:
          description: Event type of that occurrence, e.g. order.created. Set in delivery listings.
          type: string
        last_attempted_at:
          description: Timestamp of the most recent attempt, RFC3339.
          format: date-time
          type: string
        max_attempts:
          description: "Attempts allowed per round before the delivery is marked failed: the webhook's max_retries + 1 when the delivery was created. A manual retry starts a new round."
          format: int64
          type: integer
        next_retry_at:
          description: Timestamp of the next scheduled retry, RFC3339, if one is pending.
          format: date-time
          type: string
        response_body:
          description: "Endpoint response body from the most recent attempt: up to 1 KiB, or up to SPARROW_MAX_CAPTURED_RESPONSE_BYTES when the webhook has capture_response_body."
          type: string
        response_code:
          description: HTTP status code returned by the endpoint on the most recent attempt, if any.
          format: int64
          type: integer
        status:
          description: "Current delivery status. pending: queued for its first attempt (or re-queued by a retry). sending: an attempt is in flight. retrying: an attempt failed and another is scheduled (next_retry_at). success: an attempt got an expected status code. failed: attempts exhausted, or a non-retryable error such as template_error. expired: the event's ttl_seconds passed before the attempt. paused: held because the webhook or subscription was paused; not attempted until retried."
          enum:
            - pending
            - sending
            - success
            - failed
            - retrying
            - expired
            - paused
          type: string
        template_error:
          description: "The payload transform error, when the subscription's template failed to render. Set for both on_transform_error modes: with fail the delivery was not sent; with fallback the default envelope was sent instead."
          type: string
        webhook_description:
          description: Description of that webhook. Set in delivery listings.
          type: string
        webhook_id:
          description: Webhook this delivery was sent to.
          type: string
        webhook_url:
          description: URL of that webhook. Set in delivery listings.
          type: string
      required:
        - delivery_id
        - webhook_id
        - event_id
        - status
        - attempt_count
        - max_attempts
        - created_at
      type: object
    DraftTemplateBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/DraftTemplateBody.json
          format: uri
          readOnly: true
          type: string
        current_template:
          description: Optional existing template to refine instead of drafting from scratch.
          maxLength: 20000
          type: string
        docs_url:
          description: Optional http(s) page documenting the receiver's payload. Sparrow fetches it from a public address only (loopback, private and cloud-metadata addresses are always refused, even when SPARROW_ALLOW_PRIVATE_NETWORKS lets deliveries reach them) and gives the model an excerpt of its text.
          format: uri
          maxLength: 2048
          type: string
        event_name:
          description: Registered event type whose schema and sample payload ground the draft, and against which it is verified.
          type: string
        instructions:
          description: Plain-language description of the body the receiver should get, e.g. "a short Slack message with the order id and total, flagged when refunded".
          maxLength: 4000
          minLength: 1
          type: string
        recipe:
          description: Name of a shipped recipe (see GET /v1/recipes) whose destination format the draft should follow, e.g. "slack". Omit for a custom receiver.
          type: string
        sample_payload:
          additionalProperties: {}
          description: Optional payload to ground and verify the draft against instead of the event type's registered sample. Does not change the event type.
          type: object
        target_example:
          description: "Optional: what the receiver expects, either an example body (the draft maps event fields onto that exact structure) or a plain-language description of its shape."
          maxLength: 20000
          type: string
      required:
        - event_name
        - instructions
      type: object
    DraftTemplateOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/DraftTemplateOutputBody.json
          format: uri
          readOnly: true
          type: string
        attempts:
          description: Draft/render rounds it took; more than 1 means an earlier draft failed to render and was repaired.
          format: int64
          type: integer
        model:
          description: Model id that produced the draft.
          type: string
        notes:
          description: Short explanation from the model of the fields used and assumptions made.
          type: string
        rendered:
          description: The template rendered against the sample payload used (provided or registered), proving it renders.
          type: string
        sample_source:
          description: "Which sample payload grounded and verified the draft: registered or provided."
          enum:
            - registered
            - provided
          type: string
        template:
          description: The drafted Go template, ready to save as transform_template.
          type: string
      required:
        - template
        - rendered
        - sample_source
        - attempts
        - model
      type: object
    DraftTemplatePromptOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/DraftTemplatePromptOutputBody.json
          format: uri
          readOnly: true
          type: string
        prompt:
          description: "A self-contained prompt to paste into any chat assistant: the template data model, rules, helper catalog, the event's schema and sample payload, any recipe, receiver example, docs excerpt, current template, and the instructions. Asks for the template in a code block."
          type: string
        sample_source:
          description: "Which sample payload the prompt carries: registered (the event type's sample_payload) or provided (sample_payload from the request)."
          enum:
            - registered
            - provided
          type: string
      required:
        - prompt
        - sample_source
      type: object
    ErrorDetail:
      additionalProperties: false
      properties:
        location:
          description: Where the error occurred, e.g. 'body.items[3].tags' or 'path.thing-id'
          type: string
        message:
          description: Error message text
          type: string
        value:
          description: The value at the given location
      type: object
    ErrorModel:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ErrorModel.json
          format: uri
          readOnly: true
          type: string
        detail:
          description: A human-readable explanation specific to this occurrence of the problem.
          examples:
            - Property foo is required but is missing.
          type: string
        errors:
          description: Optional list of individual error details
          items:
            $ref: "#/components/schemas/ErrorDetail"
          type: array
        instance:
          description: A URI reference that identifies the specific occurrence of the problem.
          examples:
            - https://example.com/error-log/abc123
          format: uri
          type: string
        status:
          description: HTTP status code
          examples:
            - 400
          format: int64
          type: integer
        title:
          description: A short, human-readable summary of the problem type. This value should not change between occurrences of the error.
          examples:
            - Bad Request
          type: string
        type:
          default: about:blank
          description: A URI reference to human-readable documentation for the error.
          examples:
            - https://example.com/errors/example
          format: uri
          type: string
      type: object
    EventOccurrenceItem:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/EventOccurrenceItem.json
          format: uri
          readOnly: true
          type: string
        consumer:
          description: Tenant consumer this occurrence was pushed into.
          type: string
        created_at:
          description: When this occurrence was pushed, RFC3339.
          format: date-time
          type: string
        event:
          description: Event type name.
          type: string
        event_id:
          description: Event occurrence id (UUID).
          type: string
        event_version:
          description: Event type version this occurrence was accepted under.
          format: int64
          type: integer
        failed_deliveries:
          description: "Failed delivery attempts for this occurrence. Counts attempts, not deliveries: a delivery that failed twice and then succeeded adds 2 here and 1 to successful_deliveries."
          format: int32
          type: integer
        labels:
          additionalProperties:
            type: string
          description: Key/value labels used to match against subscription label filters.
          type: object
        metadata:
          additionalProperties:
            type: string
          description: Arbitrary key/value metadata stored with the occurrence.
          type: object
        payload:
          additionalProperties: {}
          description: The pushed payload.
          type: object
        pending_deliveries:
          description: "Deliveries not finished yet: pending, sending or retrying."
          format: int32
          type: integer
        scheduled_at:
          description: When this occurrence fans out to subscriptions, RFC3339. Absent for immediate pushes. Deliveries exist only from this time on.
          format: date-time
          type: string
        schema_valid:
          description: Whether the payload validated against the event type's JSON Schema at push time.
          type: boolean
        successful_deliveries:
          description: Successful delivery attempts for this occurrence.
          format: int32
          type: integer
        webhook_count:
          description: Number of distinct webhooks that got a delivery for this occurrence.
          format: int32
          type: integer
      required:
        - event_id
        - consumer
        - event
        - payload
        - schema_valid
        - event_version
        - webhook_count
        - successful_deliveries
        - failed_deliveries
        - pending_deliveries
        - created_at
      type: object
    EventTypeBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/EventTypeBody.json
          format: uri
          readOnly: true
          type: string
        active:
          description: Whether events of this type can be pushed; a push to an inactive type fails with 409. Defaults to true on register; unchanged on update when omitted.
          type: boolean
        description:
          description: Human-readable summary of what this event represents. On update, an empty string leaves it unchanged.
          type: string
        event_schema:
          additionalProperties: {}
          description: "Optional JSON Schema for the payload. Validation is soft: non-conforming payloads are still accepted, with warnings returned from the push call. On update, sending a different schema creates a new version."
          type: object
        metadata:
          additionalProperties:
            type: string
          description: Arbitrary key/value metadata for your own tooling. On update, replaces the whole map.
          type: object
        name:
          description: Unique event type name, e.g. order.created. Required on register, ignored on update (the path names the type). Lowercase letters, digits, '_' and '-' in dot-separated parts (e.g. stripe.payment_intent.succeeded), at most 255 characters; the sparrow. prefix is reserved. Immutable after creation, and event types cannot be deleted.
          type: string
      type: object
    EventTypeBundle:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/EventTypeBundle.json
          format: uri
          readOnly: true
          type: string
        apiVersion:
          description: Bundle schema version.
          enum:
            - sparrow/v1
          type: string
        items:
          description: Event type definitions, sorted by name.
          items:
            $ref: "#/components/schemas/BundleItem"
          type: array
        kind:
          description: Bundle kind.
          enum:
            - EventTypeList
          type: string
        stamp:
          $ref: "#/components/schemas/BundleStampBody"
          description: Which Sparrow produced this bundle. A compatibility hint, not a signature.
      required:
        - apiVersion
        - kind
        - stamp
        - items
      type: object
    EventTypeChange:
      additionalProperties: false
      properties:
        action:
          description: "created: new event type at version 1. new_version: the schema changed, so a new version was created and the previous one kept. updated: the current version changed in place (description, metadata, active, or a first schema added to a schema-less version). unchanged: nothing was written."
          enum:
            - created
            - new_version
            - updated
            - unchanged
          type: string
        active_change:
          description: Set when the write flips the active flag.
          enum:
            - deactivates
            - reactivates
          type: string
        affected_subscriptions:
          description: "For new_version: subscriptions that receive this event type, by name or catch-all."
          format: int64
          type: integer
        changes:
          description: "Fields that changed: schema, schema_defined (a first schema was added in place), description, metadata, active."
          items:
            type: string
          type: array
        compatibility:
          $ref: "#/components/schemas/SchemaCompatibilityItem"
          description: "For new_version: whether the schema change could break a subscription's payload transformation."
        previous_version:
          description: Version before the write; absent when the event type was created.
          format: int64
          type: integer
        version:
          description: Version after the write.
          format: int64
          type: integer
      required:
        - action
        - version
      type: object
    EventTypeItem:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/EventTypeItem.json
          format: uri
          readOnly: true
          type: string
        active:
          description: Whether events of this type can currently be pushed.
          type: boolean
        created_at:
          description: Creation timestamp, RFC3339.
          format: date-time
          type: string
        description:
          description: Human-readable summary of what this event represents.
          type: string
        event_schema:
          additionalProperties: {}
          description: JSON Schema payloads are softly validated against, if one is registered.
          type: object
        metadata:
          additionalProperties:
            type: string
          description: Arbitrary key/value metadata.
          type: object
        name:
          description: Event type name, e.g. order.created.
          type: string
        sample_payload:
          additionalProperties: {}
          description: Example payload derived from the schema, used to preview subscription transform templates.
          type: object
        updated_at:
          description: Last-modified timestamp, RFC3339.
          format: date-time
          type: string
        version:
          description: Current schema version. Starts at 1 and increases only when the schema changes; every version is kept (see listEventTypeVersions).
          format: int64
          type: integer
      required:
        - name
        - active
        - version
        - created_at
        - updated_at
      type: object
    EventTypeVersionItem:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/EventTypeVersionItem.json
          format: uri
          readOnly: true
          type: string
        created_at:
          description: When this version was created, RFC3339.
          format: date-time
          type: string
        description:
          description: Description as it was when this version was created.
          type: string
        event_schema:
          additionalProperties: {}
          description: JSON Schema of this version. Absent for a schema-less version.
          type: object
        name:
          description: Event type name.
          type: string
        sample_payload:
          additionalProperties: {}
          description: Example payload generated from this version's schema.
          type: object
        schema_defined_at:
          description: When a first schema was added to this version in place, RFC3339. Events pushed before this time were accepted while the version had no schema.
          format: date-time
          type: string
        version:
          description: Version number.
          format: int64
          type: integer
      required:
        - name
        - version
        - created_at
      type: object
    ExportEventTypesInputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ExportEventTypesInputBody.json
          format: uri
          readOnly: true
          type: string
        all:
          description: Export every event type.
          type: boolean
        names:
          description: Export these event types. Each must exist.
          items:
            type: string
          type: array
        prefix:
          description: Export every event type whose name starts with this prefix.
          type: string
      type: object
    HealthRules:
      additionalProperties: false
      properties:
        degraded_min_attempts:
          description: Attempts in the window needed before a low success rate counts as degraded.
          format: int64
          type: integer
        degraded_success_rate:
          description: A success rate below this (0.0 to 1.0), over at least degraded_min_attempts attempts, is degraded.
          format: double
          type: number
        healthy_min_attempts:
          description: Attempts at or above degraded_success_rate needed to call a webhook healthy; with fewer it stays unknown.
          format: int64
          type: integer
        unhealthy_consecutive_failures:
          description: Failed attempts in a row that make a webhook unhealthy whatever its success rate.
          format: int64
          type: integer
        unhealthy_min_attempts:
          description: Attempts in the window needed before a low success rate counts as unhealthy.
          format: int64
          type: integer
        unhealthy_success_rate:
          description: A success rate below this (0.0 to 1.0), over at least unhealthy_min_attempts attempts, is unhealthy.
          format: double
          type: number
        window_hours:
          description: Lookback window, in hours, of the attempt counts and success rate.
          format: int64
          type: integer
      required:
        - window_hours
        - unhealthy_consecutive_failures
        - unhealthy_success_rate
        - unhealthy_min_attempts
        - degraded_success_rate
        - degraded_min_attempts
        - healthy_min_attempts
      type: object
    HealthSummaryOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/HealthSummaryOutputBody.json
          format: uri
          readOnly: true
          type: string
        degraded_count:
          description: Webhooks currently labelled degraded.
          format: int64
          type: integer
        healthy_count:
          description: Webhooks currently labelled healthy.
          format: int64
          type: integer
        rules:
          $ref: "#/components/schemas/HealthRules"
          description: Thresholds the health statuses are derived from.
        unhealthy_count:
          description: Webhooks currently labelled unhealthy.
          format: int64
          type: integer
        unknown_count:
          description: "Webhooks labelled unknown: too few recent attempts to judge."
          format: int64
          type: integer
      required:
        - healthy_count
        - degraded_count
        - unhealthy_count
        - unknown_count
        - rules
      type: object
    ImportEventTypesInputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ImportEventTypesInputBody.json
          format: uri
          readOnly: true
          type: string
        acknowledge:
          description: Stamp warnings you accept. Each unacknowledged warning blocks the write.
          items:
            enum:
              - version_differs
              - format_unsupported
              - items_changed
            type: string
          type: array
        allow_breaking:
          description: Apply breaking schema changes to event types that subscriptions receive.
          type: boolean
        apiVersion:
          description: Bundle schema version, if the body is an exported bundle.
          enum:
            - sparrow/v1
          type: string
        dry_run:
          description: Compute the full result without writing anything.
          type: boolean
        items:
          description: Definitions to import. Each replaces the named event type; types not listed are never touched.
          items:
            $ref: "#/components/schemas/BundleItem"
          maxItems: 500
          minItems: 1
          type: array
        kind:
          description: Bundle kind, if the body is an exported bundle.
          enum:
            - EventTypeList
          type: string
        stamp:
          $ref: "#/components/schemas/BundleStampBody"
          description: The bundle's stamp. Absent for a hand-written bundle, which imports with a notice.
        subscription_policy:
          description: "keep_active (default): subscriptions keep running; a template that no longer fits fails its deliveries visibly with template_error. pause: in the same transaction, pause each subscription whose template fails the pre-check, with the import as the reason."
          enum:
            - keep_active
            - pause
          type: string
      required:
        - items
      type: object
    ImportEventTypesOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ImportEventTypesOutputBody.json
          format: uri
          readOnly: true
          type: string
        applied:
          description: True when the import was written. False for a dry run or when blocked_by is non-empty.
          type: boolean
        blocked_by:
          description: "Why nothing was written: unacknowledged stamp warnings, and breaking when a breaking change needs allow_breaking."
          items:
            type: string
          type: array
        dry_run:
          description: Whether this was a dry run.
          type: boolean
        imported_at:
          description: When the import was written, RFC3339. Use it to find deliveries that failed after the change.
          format: date-time
          type: string
        items:
          description: One result per bundle item, sorted by name.
          items:
            $ref: "#/components/schemas/ImportItemResult"
          type: array
        stamp:
          $ref: "#/components/schemas/StampCheckItem"
          description: The bundle's stamp compared with this server.
      required:
        - applied
        - dry_run
        - stamp
        - items
      type: object
    ImportItemResult:
      additionalProperties: false
      properties:
        action:
          description: What the import does to this event type. See updateEventType's change.action.
          enum:
            - created
            - new_version
            - updated
            - unchanged
          type: string
        active_change:
          description: Set when the import flips the active flag.
          enum:
            - deactivates
            - reactivates
          type: string
        blocked:
          description: The change is breaking for subscriptions and allow_breaking was not set.
          type: boolean
        changes:
          description: "Fields that change: schema, schema_defined, description, metadata, active."
          items:
            type: string
          type: array
        compatibility:
          $ref: "#/components/schemas/SchemaCompatibilityItem"
          description: "For new_version: whether the change could break a subscription's payload transformation."
        name:
          description: Event type name.
          type: string
        previous_version:
          description: Version before the import.
          format: int64
          type: integer
        subscriptions:
          $ref: "#/components/schemas/TemplateCheckItem"
          description: "For new_version: every affected subscription's transform rendered strictly against the new version."
        version:
          description: Version after the import (or that it would have, for a dry run).
          format: int64
          type: integer
      required:
        - name
        - action
        - version
      type: object
    InviteOut:
      additionalProperties: false
      properties:
        cancelled_at:
          description: "When the invite was cancelled, RFC3339. Null: not cancelled."
          format: date-time
          type:
            - string
            - "null"
        consumer:
          description: "Consumer the resulting token is limited to. Null: tenant-wide."
          type:
            - string
            - "null"
        created_at:
          description: When the invite was created, RFC3339.
          format: date-time
          type: string
        created_by:
          description: Name of the credential that created the invite.
          type: string
        expires_at:
          description: When the invite stops working if unused, RFC3339.
          format: date-time
          type: string
        id:
          description: Invite id (not the secret).
          type: string
        name:
          description: Name given to the token the invite creates.
          type: string
        redeemed_at:
          description: "When the invite was redeemed, RFC3339. Null: not redeemed."
          format: date-time
          type:
            - string
            - "null"
        status:
          description: "pending: can still be redeemed. redeemed: used once, see token_id. cancelled: cancelled by DELETE /v1/invites/{invite_id}. expired: past expires_at unused."
          enum:
            - pending
            - redeemed
            - cancelled
            - expired
          type: string
        token_id:
          description: Token created when the invite was redeemed.
          type:
            - string
            - "null"
        token_ttl_seconds:
          description: "Lifetime of the token it creates, in seconds. Null: the token never expires."
          format: int64
          type:
            - integer
            - "null"
      required:
        - id
        - name
        - consumer
        - status
        - token_ttl_seconds
        - created_by
        - created_at
        - expires_at
        - redeemed_at
        - cancelled_at
        - token_id
      type: object
    ListAlertConfigsOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ListAlertConfigsOutputBody.json
          format: uri
          readOnly: true
          type: string
        items:
          description: Alert configs, oldest first. Not paginated.
          items:
            $ref: "#/components/schemas/AlertConfigItem"
          type: array
      required:
        - items
      type: object
    ListConsumersOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ListConsumersOutputBody.json
          format: uri
          readOnly: true
          type: string
        items:
          description: Matching consumer names, sorted by name.
          items:
            $ref: "#/components/schemas/ConsumerItem"
          type: array
      required:
        - items
      type: object
    ListCrlsOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ListCrlsOutputBody.json
          format: uri
          readOnly: true
          type: string
        crls:
          description: CRLs this instance has cached, by URL. Each instance keeps its own cache.
          items:
            $ref: "#/components/schemas/CrlOut"
          type: array
        mode:
          description: "SPARROW_TLS_REVOCATION: soft allows a connection whose CRL is unavailable, hard refuses it, off skips the check."
          enum:
            - soft
            - hard
            - "off"
          type: string
      required:
        - mode
        - crls
      type: object
    ListDeliveriesOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ListDeliveriesOutputBody.json
          format: uri
          readOnly: true
          type: string
        items:
          description: Deliveries, newest first. Includes deliveries of deleted webhooks.
          items:
            $ref: "#/components/schemas/DeliveryItem"
          type: array
        pagination:
          $ref: "#/components/schemas/CursorPaginationOutput"
          description: Cursor pagination; there is no total count.
        retry_id:
          description: Snapshot (batch job) id, present when prepare_retry was set and something matched. Send it as the retry_id field of POST .../deliveries:retryBatch. Expires after 15 minutes if not started.
          type: string
        retry_total:
          description: Number of deliveries in that snapshot (all matches, not just this page).
          format: int64
          type: integer
      required:
        - items
        - pagination
      type: object
    ListEventOccurrencesOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ListEventOccurrencesOutputBody.json
          format: uri
          readOnly: true
          type: string
        items:
          description: Occurrences, newest first.
          items:
            $ref: "#/components/schemas/EventOccurrenceItem"
          type: array
        pagination:
          $ref: "#/components/schemas/CursorPaginationOutput"
          description: Cursor pagination; there is no total count.
        repush_id:
          description: Snapshot (batch job) id for POST .../events:rePush, present when prepare_repush was set and something matched. Expires after 15 minutes if not started.
          type: string
        repush_total:
          description: Number of occurrences in that snapshot (all matches, not just this page).
          format: int64
          type: integer
      required:
        - items
        - pagination
      type: object
    ListEventTypeVersionsOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ListEventTypeVersionsOutputBody.json
          format: uri
          readOnly: true
          type: string
        items:
          description: Every version of the event type, newest first.
          items:
            $ref: "#/components/schemas/EventTypeVersionItem"
          type: array
      required:
        - items
      type: object
    ListEventTypesOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ListEventTypesOutputBody.json
          format: uri
          readOnly: true
          type: string
        items:
          description: Event types, sorted by name.
          items:
            $ref: "#/components/schemas/EventTypeItem"
          type: array
        pagination:
          $ref: "#/components/schemas/PaginationOutput"
          description: Offset pagination with a total count.
      required:
        - items
        - pagination
      type: object
    ListInvitesOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ListInvitesOutputBody.json
          format: uri
          readOnly: true
          type: string
        items:
          description: Invites, newest first. Not paginated.
          items:
            $ref: "#/components/schemas/InviteOut"
          type: array
      required:
        - items
      type: object
    ListRecipesOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ListRecipesOutputBody.json
          format: uri
          readOnly: true
          type: string
        items:
          description: Every shipped recipe.
          items:
            $ref: "#/components/schemas/Recipe"
          type: array
      required:
        - items
      type: object
    ListSubscriptionsOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ListSubscriptionsOutputBody.json
          format: uri
          readOnly: true
          type: string
        items:
          description: Subscriptions, newest first.
          items:
            $ref: "#/components/schemas/SubscriptionItem"
          type: array
        pagination:
          $ref: "#/components/schemas/PaginationOutput"
          description: Offset pagination with a total count.
      required:
        - items
        - pagination
      type: object
    ListTemplateVersionsOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ListTemplateVersionsOutputBody.json
          format: uri
          readOnly: true
          type: string
        items:
          description: Saved versions, newest first.
          items:
            $ref: "#/components/schemas/TemplateVersionItem"
          type: array
      required:
        - items
      type: object
    ListTokensOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ListTokensOutputBody.json
          format: uri
          readOnly: true
          type: string
        items:
          description: Tokens, newest first. Not paginated.
          items:
            $ref: "#/components/schemas/TokenOut"
          type: array
      required:
        - items
      type: object
    ListWebhooksOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ListWebhooksOutputBody.json
          format: uri
          readOnly: true
          type: string
        items:
          description: Webhooks, newest first.
          items:
            $ref: "#/components/schemas/WebhookOut"
          type: array
        pagination:
          $ref: "#/components/schemas/PaginationOutput"
          description: Offset pagination with a total count.
      required:
        - items
        - pagination
      type: object
    PaginationOutput:
      additionalProperties: false
      properties:
        has_more:
          description: True when offset + limit < total_count.
          type: boolean
        limit:
          description: Page size applied (the requested limit, default 50).
          format: int32
          type: integer
        offset:
          description: Number of items skipped.
          format: int32
          type: integer
        total_count:
          description: Total items matching the filters.
          format: int32
          type: integer
      required:
        - limit
        - offset
        - total_count
        - has_more
      type: object
    Param:
      additionalProperties: false
      properties:
        activation_required:
          description: A missing value blocks enabling an auto-provisioned recipe.
          type: boolean
        default:
          description: Value used when none is supplied.
          type: string
        docs_url:
          description: Destination documentation for this value.
          type: string
        enum:
          description: The only accepted values, when set.
          items:
            type: string
          type: array
        example:
          description: A sample value.
          type: string
        help:
          description: What the value is and where to find it.
          type: string
        must_override_default:
          description: "The default is a placeholder: it must be replaced with a real value before the recipe is applied."
          type: boolean
        name:
          description: Param name, as referenced by {{param "name"}}.
          type: string
        prompt:
          description: Short label shown for the param.
          type: string
        required:
          description: Whether a value must be supplied.
          type: boolean
        secret:
          description: Whether the value is sensitive (a token or password); clients should mask the input.
          type: boolean
      required:
        - name
        - prompt
        - required
      type: object
    PatchSubscriptionBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/PatchSubscriptionBody.json
          format: uri
          readOnly: true
          type: string
        headers:
          additionalProperties:
            type: string
          description: Replace all extra HTTP headers.
          type: object
        label_filters:
          additionalProperties:
            type: string
          description: Replace all label filters; an empty object matches every event.
          type: object
        method:
          description: Replace the HTTP method.
          type: string
        on_transform_error:
          description: Replace what happens when the template fails to render.
          enum:
            - fail
            - fallback
          type: string
        template_missing_key:
          description: Replace how the template reads a missing key.
          enum:
            - error
            - zero
          type: string
        template_notes:
          description: Optional note stored with the template version, e.g. the AI drafter's summary.
          maxLength: 2000
          type: string
        template_source:
          description: How a changed transform_template was produced, recorded in the subscription's template history. Defaults to manual.
          enum:
            - manual
            - ai_draft
          type: string
        timeout:
          description: Replace the request timeout override, in seconds (0 to 300; 0 uses the webhook's).
          format: int64
          type: integer
        transform_enabled:
          description: Enable or disable payload transformation.
          type: boolean
        transform_template:
          description: Replace the transform template. A template that does not parse is rejected with 400. A change is recorded as a new version in the template history (the last 20 are kept).
          type: string
      type: object
    PatchWebhookBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/PatchWebhookBody.json
          format: uri
          readOnly: true
          type: string
        active:
          description: false pauses the webhook (like :pause), true resumes it and clears any auto-disable marker (like :resume, but without returning the held count).
          type: boolean
        description:
          description: Replace the human-readable description. An empty string leaves it unchanged.
          type: string
        events:
          description: Replace the full set of subscribed event type names. Every existing subscription of the webhook is deleted, with its transform and label filters, and one plain subscription is created per name. An empty list leaves subscriptions unchanged. Rejected with 409 while requires_transform is on; to keep transforms, add or delete subscriptions individually.
          items:
            type: string
          type: array
        headers:
          additionalProperties:
            type: string
          description: Replace all static headers sent with every delivery. Same rules as on register.
          type: object
        http_config:
          $ref: "#/components/schemas/WebhookHTTPConfig"
          description: Update HTTP delivery settings. Only the fields you send change; the rest keep their current values. The merged result must satisfy the same limits as on create, or the request is rejected with 400.
        requires_transform:
          description: "Require (or stop requiring) a payload transform on every subscription. Turning it on fails with 409 while any subscription has no enabled transform_template. While on, events cannot be replaced in bulk (409): add or delete subscriptions individually."
          type: boolean
        secret_headers:
          additionalProperties:
            type: string
          description: Merge-patch encrypted secret headers by name. Send a new value to replace one header, omit a key to leave it untouched, or send an empty string to remove it. Names match in any case. An empty object removes them all. Changing url to another origin (scheme, host or port) is refused unless this sets every existing secret header again (a new value, or an empty string to remove it), so moving a webhook never carries its secrets to a new server.
          type: object
        signature_type:
          description: "Change the signing scheme: hmac or ed25519. Switching to ed25519 generates a signing keypair; HMAC signing continues either way."
          type: string
        timeout:
          deprecated: true
          description: "Deprecated: use http_config.request_timeout_seconds. Shorthand for it, in seconds (1 to 300), applied only when http_config.request_timeout_seconds is not sent (http_config wins when both are sent)."
          format: int64
          type: integer
        url:
          description: Replace the delivery endpoint URL. Same rules as on register.
          type: string
      type: object
    PauseSubscriptionInputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/PauseSubscriptionInputBody.json
          format: uri
          readOnly: true
          type: string
        reason:
          description: "Why the subscription is paused, shown next to it, for example: receiver maintenance until Friday."
          maxLength: 500
          type: string
      type: object
    PortalStruct:
      additionalProperties: false
      properties:
        retry:
          description: True when portal tokens may retry their deliveries (SPARROW_PORTAL_RETRY, default true). When false, the retry calls under /portal/api/ answer 403; admins can still retry.
          type: boolean
      required:
        - retry
      type: object
    PushEventBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/PushEventBody.json
          format: uri
          readOnly: true
          type: string
        idempotency_key:
          description: "Client-supplied deduplication key, unique per consumer. A push whose key matches an earlier event in the same consumer still returns 201, with duplicate: true and the original event_id, and creates no event or deliveries. The key is kept as long as the event (see SPARROW_EVENT_RETENTION_DAYS)."
          type: string
        labels:
          additionalProperties:
            type: string
          description: Key/value labels a subscription's label_filters match against to decide which subscriptions receive this occurrence. At most 20 pairs; keys up to 64 characters of letters, digits, '.', '_' and '-'; values up to 256 characters.
          type: object
        metadata:
          additionalProperties:
            type: string
          description: Arbitrary key/value metadata stored with the occurrence.
          type: object
        payload:
          additionalProperties: {}
          description: Event payload, a JSON object. Validated softly against the event type's JSON Schema if one is registered. The whole request body is limited to 1 MiB (413 above that).
          type: object
        scheduled_at:
          description: RFC 3339 time to deliver the event at. Omitted or in the past means immediate. The event is stored right away but fans out to subscriptions only at this time; ttl_seconds counts from then. At most one year ahead (400 beyond that). Replays of a scheduled occurrence are immediate.
          format: date-time
          type: string
        ttl_seconds:
          description: Time-to-live in seconds, counted from fan-out. A delivery attempt that would start after it expires is not made and the delivery fails as expired. 0 or omitted means no expiry. Manual retries ignore it.
          format: int64
          type: integer
      required:
        - payload
      type: object
    PushEventOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/PushEventOutputBody.json
          format: uri
          readOnly: true
          type: string
        duplicate:
          description: True when idempotency_key matched an existing event occurrence in this consumer; no new event or deliveries were created and the status is still 201.
          type: boolean
        event_id:
          description: Id (UUID) of the created event occurrence. When duplicate is true, this is the id of the PREVIOUSLY created event, not a new one.
          type: string
        schema_valid:
          description: Whether the payload validated against the event type's JSON Schema (true when it has none). False does not mean the push was rejected; see warnings. For a duplicate, the original event's result.
          type: boolean
        warnings:
          description: Non-fatal schema validation warnings. The event is stored and delivered regardless. Absent for a duplicate.
          items:
            type: string
          type: array
      required:
        - event_id
        - duplicate
        - schema_valid
      type: object
    Recipe:
      additionalProperties: false
      properties:
        consumer:
          $ref: "#/components/schemas/ConsumerHint"
          description: The consumer the recipe is meant to be registered under, when it matters.
        description:
          description: One-line summary of the destination the recipe targets.
          type: string
        guidance:
          description: What an ideal message for this destination looks like. Advice for template authors and AI drafting, not a rule.
          type: string
        name:
          description: Recipe name, e.g. slack. Unique among shipped recipes.
          type: string
        params:
          description: Values the user supplies when applying the recipe. Each {{param "name"}} token in webhook.url, webhook.headers, webhook.secret_headers and subscription.transform_template is replaced with the value by plain string substitution before registration.
          items:
            $ref: "#/components/schemas/Param"
          type: array
        subscription:
          $ref: "#/components/schemas/Subscription"
          description: The transform given to the subscriptions created for the webhook.
        version:
          description: Recipe schema version. Always 1.
          format: int64
          type: integer
        webhook:
          $ref: "#/components/schemas/Webhook"
          description: The webhook to register.
      required:
        - version
        - name
        - description
        - webhook
        - subscription
      type: object
    RegisterWebhookBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/RegisterWebhookBody.json
          format: uri
          readOnly: true
          type: string
        active:
          description: Whether the webhook is sent deliveries. Defaults to true. An inactive (paused) webhook still gets deliveries recorded for new events, held with status paused and sent only when retried.
          type: boolean
        description:
          description: Free-text note for humans, e.g. which system or team owns this endpoint.
          type: string
        events:
          description: Event type names this webhook should receive; auto-creates one subscription per entry. Use "*" as the sole entry to subscribe to every event in the consumer. Omit or leave empty to register the webhook with no subscriptions, then attach them individually via POST .../subscriptions (e.g. to set a per-subscription transform_template).
          items:
            type: string
          type: array
        headers:
          additionalProperties: {}
          description: "Static HTTP headers sent with every delivery to this webhook. Only string values are sent. Names must be valid HTTP tokens, values at most 8 KiB with no CR/LF; framing headers (Host, Content-Length, Transfer-Encoding, Connection, Keep-Alive, Proxy-Connection, TE, Trailer, Upgrade) are rejected. Stored and returned in plaintext: put credentials in secret_headers."
          type: object
        http_config:
          $ref: "#/components/schemas/WebhookHTTPConfig"
          description: Per-webhook HTTP delivery tuning (retries, timeouts, rate limit). Falls back to server defaults for any field left unset.
        on_transform_error:
          description: What happens when transform_template fails to render for one of the created subscriptions. fail (default) or fallback; see the subscription's on_transform_error.
          enum:
            - fail
            - fallback
          type: string
        rate_limit_rps:
          deprecated: true
          description: "Deprecated: use http_config.rate_limit_rps. Shorthand for it, applied only when http_config.rate_limit_rps is not set (http_config wins when both are sent). Requests per second, must be positive."
          format: double
          type: number
        requires_transform:
          description: "Set when the receiver only accepts a transformed payload (Slack, SendGrid, ...). Every subscription must then have transform_enabled and a transform_template: transform_template is required here when events is non-empty, and later subscription writes without one are rejected. Recipes set this. Defaults to false."
          type: boolean
        secret_headers:
          additionalProperties:
            type: string
          description: HTTP headers sent with every delivery whose values are envelope-encrypted at rest and masked in every API response (e.g. an upstream auth token). Same name and value rules as headers. Names are case-insensitive and saved in canonical form (x-api-key becomes X-Api-Key); the same name twice in different case is refused.
          type: object
        signature_type:
          description: Signing scheme. Every delivery carries an HMAC-SHA256 signature (v1,) in the Standard Webhooks webhook-signature header; ed25519 also generates a signing keypair and adds an Ed25519 signature (v1a,) next to it. Defaults to hmac.
          enum:
            - hmac
            - ed25519
          type: string
        template_missing_key:
          description: How transform_template reads a key the payload does not have, for the created subscriptions. error (default) or zero; see the subscription's template_missing_key.
          enum:
            - error
            - zero
          type: string
        template_notes:
          description: Optional note stored with that first template version, e.g. the AI drafter's summary.
          maxLength: 2000
          type: string
        template_source:
          description: How transform_template was produced, recorded as the first version in each created subscription's template history. Defaults to manual.
          enum:
            - manual
            - ai_draft
          type: string
        transform_template:
          description: Transform template given to every subscription created from events, with transform_enabled set. A template that does not parse is rejected with 400. Omit to create them untransformed (not allowed with requires_transform).
          type: string
        url:
          description: "http or https endpoint deliveries are POSTed to. The host must resolve: localhost, *.internal, *.local, private, loopback and cloud metadata addresses are rejected (SSRF protection) unless allowed by SPARROW_ALLOW_PRIVATE_NETWORKS or SPARROW_ALLOWED_NETWORKS."
          format: uri
          type: string
      required:
        - url
      type: object
    RepushBatchInputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/RepushBatchInputBody.json
          format: uri
          readOnly: true
          type: string
        repush_id:
          description: repush_id returned by an earlier GET .../events?prepare_repush=true call.
          type: string
      required:
        - repush_id
      type: object
    RepushEventOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/RepushEventOutputBody.json
          format: uri
          readOnly: true
          type: string
        event_id:
          description: Id (UUID) of the new event occurrence created by the replay. The original occurrence is unchanged.
          type: string
        warnings:
          description: Non-fatal warnings from validating the stored payload against the event type's current schema.
          items:
            type: string
          type: array
      required:
        - event_id
      type: object
    ResumeSubscriptionOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ResumeSubscriptionOutputBody.json
          format: uri
          readOnly: true
          type: string
        consumer:
          description: Consumer this subscription belongs to.
          type: string
        created_at:
          description: Creation timestamp, RFC3339.
          format: date-time
          type: string
        event_name:
          description: Event type name this subscription matches, or "*" for catch-all.
          type: string
        headers:
          additionalProperties:
            type: string
          description: Extra HTTP headers sent with deliveries from this subscription.
          type: object
        label_filters:
          additionalProperties:
            type: string
          description: Key/value pairs that must all be present in an event's labels for this subscription to receive it.
          type: object
        method:
          description: HTTP method used for deliveries from this subscription; absent means POST.
          enum:
            - GET
            - POST
            - PUT
            - PATCH
            - DELETE
          type: string
        on_transform_error:
          description: "What happens when transform_template fails to render. fail (default): the delivery fails with error category template_error, is not retried automatically, and can be retried once the template is fixed; it does not affect the webhook's health. fallback: the default envelope payload is sent instead, and the error is still recorded on the delivery."
          enum:
            - fail
            - fallback
          type: string
        paused:
          description: Whether the subscription is paused. While paused, events still fan out to it, but each delivery is recorded with status paused and not attempted until retried. A pause never affects the webhook's health.
          type: boolean
        paused_at:
          description: When the current pause began, RFC3339. Absent when not paused.
          format: date-time
          type: string
        paused_deliveries:
          description: Deliveries of this subscription currently in status paused. They are not sent automatically; retry them to deliver.
          format: int64
          type: integer
        paused_reason:
          description: Why the subscription was paused.
          type: string
        paused_since:
          description: When the pause that just ended began, RFC3339; absent when the subscription was not paused. List or retry what was held with status=paused&subscription_id=...&created_after=<paused_since>.
          format: date-time
          type: string
        subscription_id:
          description: Subscription id (UUID).
          type: string
        template_missing_key:
          description: "How transform_template reads a key the payload does not have. error (default): the render fails, so a field removed from the event schema cannot silently turn into \"<no value>\" in the body; read optional fields with index or dig (default only fills a key that is present but empty). zero: the key renders as \"<no value>\"."
          enum:
            - error
            - zero
          type: string
        timeout:
          description: Request timeout override for this subscription's deliveries, in seconds; absent means the webhook's request_timeout_seconds.
          format: int64
          type: integer
        transform_enabled:
          description: Whether transform_template is rendered into the delivered payload.
          type: boolean
        transform_template:
          description: Go text/template rendered against the event to produce the delivered body when transform_enabled is true.
          type: string
        updated_at:
          description: Last-modified timestamp, RFC3339.
          format: date-time
          type: string
        webhook_id:
          description: Webhook this subscription delivers to.
          type: string
      required:
        - paused_deliveries
        - subscription_id
        - consumer
        - webhook_id
        - event_name
        - transform_enabled
        - on_transform_error
        - template_missing_key
        - paused
        - created_at
        - updated_at
      type: object
    ResumeWebhookOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ResumeWebhookOutputBody.json
          format: uri
          readOnly: true
          type: string
        paused_deliveries:
          description: Deliveries held as paused for this webhook. They are not sent automatically; retry them to deliver.
          format: int64
          type: integer
      required:
        - paused_deliveries
      type: object
    RetryBatchInputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/RetryBatchInputBody.json
          format: uri
          readOnly: true
          type: string
        retry_id:
          description: retry_id returned by an earlier GET .../deliveries?prepare_retry=true call.
          type: string
      required:
        - retry_id
      type: object
    RetryDeliveriesByWebhookInputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/RetryDeliveriesByWebhookInputBody.json
          format: uri
          readOnly: true
          type: string
        force:
          description: false (default) retries deliveries in status failed, pending, retrying or paused. true retries every delivery of the webhook, including successful and expired ones, which resends them.
          type: boolean
        webhook_id:
          description: Webhook id (UUID) whose deliveries to retry.
          type: string
      required:
        - webhook_id
      type: object
    RetryOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/RetryOutputBody.json
          format: uri
          readOnly: true
          type: string
        count:
          description: Number of deliveries queued for another round of attempts.
          format: int32
          type: integer
        delivery_ids:
          description: Ids of the deliveries queued. Absent when nothing matched.
          items:
            type: string
          type: array
      required:
        - count
      type: object
    RotateWebhookSecretOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/RotateWebhookSecretOutputBody.json
          format: uri
          readOnly: true
          type: string
        webhook_secret:
          description: The new HMAC signing secret, in plaintext. Shown only in this response.
          type: string
      required:
        - webhook_secret
      type: object
    SchemaCompatibilityItem:
      additionalProperties: false
      properties:
        reasons:
          description: Each breaking change with the path it affects.
          items:
            type: string
          type: array
        result:
          description: "breaking when the change could break a payload transformation: a required property removed or made optional, a type widened, the schema removed, or a change under oneOf/anyOf/allOf/$ref/patternProperties that cannot be checked."
          enum:
            - compatible
            - breaking
          type: string
      required:
        - result
      type: object
    SendTestEventInputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/SendTestEventInputBody.json
          format: uri
          readOnly: true
          type: string
        event_name:
          description: "Event to send: the subscription's event, or any event for a catch-all (*) subscription."
          type: string
        payload:
          additionalProperties: {}
          description: Event payload to send instead of the event type's sample payload. It is rendered through the subscription's transform like a pushed event.
          type: object
      required:
        - event_name
      type: object
    SendTestEventOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/SendTestEventOutputBody.json
          format: uri
          readOnly: true
          type: string
        error_class:
          description: "Delivery error category when the send did not get a 2xx: client_error, server_error, rate_limited, timeout, connection_refused, dns_error, tls_error, network_error, redirect, template_error (nothing was sent) or unknown."
          type: string
        latency_ms:
          description: Time to the response headers, in milliseconds.
          format: int64
          type: integer
        status_code:
          description: The receiver's HTTP status; 0 when no response was received.
          format: int64
          type: integer
      required:
        - status_code
        - latency_ms
      type: object
    StampCheckItem:
      additionalProperties: false
      properties:
        exported_by:
          description: Sparrow version that exported the file.
          type: string
        server:
          description: This server's Sparrow version.
          type: string
        status:
          description: "valid: stamp present and matching. unsigned: no stamp (hand-written file). warning: see warnings."
          enum:
            - valid
            - unsigned
            - warning
          type: string
        warnings:
          description: version_differs, format_unsupported, items_changed. Each must be acknowledged for the import to write.
          items:
            type: string
          type: array
      required:
        - status
        - server
      type: object
    Subscription:
      additionalProperties: false
      properties:
        transform_template:
          description: Go template rendered per delivery to build the destination-native body; may contain {{param}} tokens.
          type: string
      required:
        - transform_template
      type: object
    SubscriptionItem:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/SubscriptionItem.json
          format: uri
          readOnly: true
          type: string
        consumer:
          description: Consumer this subscription belongs to.
          type: string
        created_at:
          description: Creation timestamp, RFC3339.
          format: date-time
          type: string
        event_name:
          description: Event type name this subscription matches, or "*" for catch-all.
          type: string
        headers:
          additionalProperties:
            type: string
          description: Extra HTTP headers sent with deliveries from this subscription.
          type: object
        label_filters:
          additionalProperties:
            type: string
          description: Key/value pairs that must all be present in an event's labels for this subscription to receive it.
          type: object
        method:
          description: HTTP method used for deliveries from this subscription; absent means POST.
          enum:
            - GET
            - POST
            - PUT
            - PATCH
            - DELETE
          type: string
        on_transform_error:
          description: "What happens when transform_template fails to render. fail (default): the delivery fails with error category template_error, is not retried automatically, and can be retried once the template is fixed; it does not affect the webhook's health. fallback: the default envelope payload is sent instead, and the error is still recorded on the delivery."
          enum:
            - fail
            - fallback
          type: string
        paused:
          description: Whether the subscription is paused. While paused, events still fan out to it, but each delivery is recorded with status paused and not attempted until retried. A pause never affects the webhook's health.
          type: boolean
        paused_at:
          description: When the current pause began, RFC3339. Absent when not paused.
          format: date-time
          type: string
        paused_reason:
          description: Why the subscription was paused.
          type: string
        subscription_id:
          description: Subscription id (UUID).
          type: string
        template_missing_key:
          description: "How transform_template reads a key the payload does not have. error (default): the render fails, so a field removed from the event schema cannot silently turn into \"<no value>\" in the body; read optional fields with index or dig (default only fills a key that is present but empty). zero: the key renders as \"<no value>\"."
          enum:
            - error
            - zero
          type: string
        timeout:
          description: Request timeout override for this subscription's deliveries, in seconds; absent means the webhook's request_timeout_seconds.
          format: int64
          type: integer
        transform_enabled:
          description: Whether transform_template is rendered into the delivered payload.
          type: boolean
        transform_template:
          description: Go text/template rendered against the event to produce the delivered body when transform_enabled is true.
          type: string
        updated_at:
          description: Last-modified timestamp, RFC3339.
          format: date-time
          type: string
        webhook_id:
          description: Webhook this subscription delivers to.
          type: string
      required:
        - subscription_id
        - consumer
        - webhook_id
        - event_name
        - transform_enabled
        - on_transform_error
        - template_missing_key
        - paused
        - created_at
        - updated_at
      type: object
    TemplateCheckItem:
      additionalProperties: false
      properties:
        catch_all:
          description: How many of the affected subscriptions are catch-all ("*").
          format: int64
          type: integer
        failures:
          description: Subscriptions whose transform no longer renders against the new version.
          items:
            $ref: "#/components/schemas/TemplateFailureItem"
          type: array
        passed:
          description: Subscriptions whose transform renders.
          format: int64
          type: integer
        paused:
          description: Subscriptions this import paused (subscription_policy=pause).
          items:
            type: string
          type: array
        without_transform:
          description: Subscriptions with no transform. Sparrow passes the payload through and cannot tell whether the receiver copes.
          format: int64
          type: integer
      required:
        - passed
        - without_transform
        - catch_all
      type: object
    TemplateFailureItem:
      additionalProperties: false
      properties:
        consumer:
          description: Consumer the subscription belongs to.
          type: string
        error:
          description: The render error.
          type: string
        payload:
          description: "Which generated payload failed: every field, or only required fields (a template reading an optional field without guarding for it)."
          enum:
            - sample
            - required_only
          type: string
        subscription_id:
          description: Subscription whose template failed.
          type: string
        webhook_id:
          description: Webhook the subscription delivers to.
          type: string
      required:
        - subscription_id
        - consumer
        - webhook_id
        - payload
        - error
      type: object
    TemplateFunctionItem:
      additionalProperties: false
      properties:
        description:
          description: Markdown documentation for the function, including usage and an example.
          type: string
        name:
          description: Function name, as used in a transform_template.
          type: string
      required:
        - name
        - description
      type: object
    TemplateFunctionsOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/TemplateFunctionsOutputBody.json
          format: uri
          readOnly: true
          type: string
        items:
          description: Every helper function, in the engine's own order.
          items:
            $ref: "#/components/schemas/TemplateFunctionItem"
          type: array
      required:
        - items
      type: object
    TemplateVersionItem:
      additionalProperties: false
      properties:
        created_at:
          description: When it was saved, RFC3339.
          format: date-time
          type: string
        current:
          description: True for the newest version, which is the subscription's current template.
          type: boolean
        notes:
          description: Note stored with the version.
          type: string
        saved_by:
          description: Name of the master key or access token that saved it; absent when auth is off.
          type: string
        source:
          description: "How the template was produced: manual or ai_draft."
          enum:
            - manual
            - ai_draft
          type: string
        template:
          description: The template text as saved.
          type: string
        version_id:
          description: Version id (UUID).
          type: string
      required:
        - version_id
        - template
        - source
        - created_at
        - current
      type: object
    TestTemplateBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/TestTemplateBody.json
          format: uri
          readOnly: true
          type: string
        event_name:
          description: Registered event type. Its name is the envelope's event_name, and its sample_payload (generated from its schema; empty without one) is rendered against when payload is omitted.
          type: string
        payload:
          additionalProperties: {}
          description: Payload to render against instead of the event type's generated sample, e.g. a real event's payload from listEventOccurrences. Used as given, even when empty; the event type needs no schema.
          type: object
        template:
          description: Go template to render, in the same syntax used by transform_template.
          type: string
        template_missing_key:
          description: Render as a subscription with this template_missing_key would. Defaults to error, the subscription default.
          enum:
            - error
            - zero
          type: string
      required:
        - event_name
        - template
      type: object
    TestTemplateOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/TestTemplateOutputBody.json
          format: uri
          readOnly: true
          type: string
        rendered:
          description: The template output, rendered against payload (or the event type's sample payload) with a placeholder event id and the current time.
          type: string
      required:
        - rendered
      type: object
    TokenOut:
      additionalProperties: false
      properties:
        consumer:
          description: "Consumer the token is limited to (portal API only). Null: tenant-wide, same power as SPARROW_API_KEY."
          type:
            - string
            - "null"
        created_at:
          description: When the token was created, RFC3339.
          format: date-time
          type: string
        created_by:
          description: Name of the credential that created it (the master key, or another token).
          type: string
        expires_at:
          description: "When the token stops working, RFC3339. Null: never expires."
          format: date-time
          type:
            - string
            - "null"
        id:
          description: Token id (not the secret).
          type: string
        last_used_at:
          description: "When the token was last used, RFC3339; approximate (updated at most once a minute). Null: never used."
          format: date-time
          type:
            - string
            - "null"
        name:
          description: Who or what the token is for.
          type: string
        revoked_at:
          description: "When the token was revoked, RFC3339. Null: not revoked."
          format: date-time
          type:
            - string
            - "null"
        status:
          description: "active: usable. revoked: revoked by DELETE /v1/tokens/{token_id}. expired: past expires_at."
          enum:
            - active
            - revoked
            - expired
          type: string
      required:
        - id
        - name
        - consumer
        - status
        - created_by
        - created_at
        - expires_at
        - revoked_at
        - last_used_at
      type: object
    UpdateEventTypeOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/UpdateEventTypeOutputBody.json
          format: uri
          readOnly: true
          type: string
        active:
          description: Whether events of this type can currently be pushed.
          type: boolean
        change:
          $ref: "#/components/schemas/EventTypeChange"
          description: What this update did.
        created_at:
          description: Creation timestamp, RFC3339.
          format: date-time
          type: string
        description:
          description: Human-readable summary of what this event represents.
          type: string
        event_schema:
          additionalProperties: {}
          description: JSON Schema payloads are softly validated against, if one is registered.
          type: object
        metadata:
          additionalProperties:
            type: string
          description: Arbitrary key/value metadata.
          type: object
        name:
          description: Event type name, e.g. order.created.
          type: string
        sample_payload:
          additionalProperties: {}
          description: Example payload derived from the schema, used to preview subscription transform templates.
          type: object
        updated_at:
          description: Last-modified timestamp, RFC3339.
          format: date-time
          type: string
        version:
          description: Current schema version. Starts at 1 and increases only when the schema changes; every version is kept (see listEventTypeVersions).
          format: int64
          type: integer
      required:
        - change
        - name
        - active
        - version
        - created_at
        - updated_at
      type: object
    ValidateEventPayloadInputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ValidateEventPayloadInputBody.json
          format: uri
          readOnly: true
          type: string
        payload:
          additionalProperties: {}
          description: Payload to validate against the event type's registered JSON Schema.
          type: object
      required:
        - payload
      type: object
    ValidateEventPayloadOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/ValidateEventPayloadOutputBody.json
          format: uri
          readOnly: true
          type: string
        valid:
          description: Whether the payload matches the event type's JSON Schema. Always true when the event type has no schema registered.
          type: boolean
        warnings:
          description: Per-field validation errors, present when valid is false.
          items:
            type: string
          type: array
      required:
        - valid
      type: object
    Webhook:
      additionalProperties: false
      properties:
        headers:
          additionalProperties:
            type: string
          description: Static headers sent with every delivery; values may contain {{param}} tokens.
          type: object
        requires_transform:
          description: The destination only accepts the transformed payload; the webhook is registered with requires_transform.
          type: boolean
        secret_headers:
          additionalProperties:
            type: string
          description: Headers whose values are encrypted at rest and masked in API responses (upstream tokens, passwords); values may contain {{param}} tokens.
          type: object
        url:
          description: Destination URL; may contain {{param}} tokens.
          type: string
      required:
        - url
      type: object
    WebhookHTTPConfig:
      additionalProperties: false
      properties:
        capture_response_body:
          description: "How much of the endpoint's response body is stored with each attempt: false (default) keeps up to 1 KiB, true up to SPARROW_MAX_CAPTURED_RESPONSE_BYTES (default 1 MiB)."
          type: boolean
        content_type:
          description: "Stored and returned, but deliveries do not use it yet: they send Content-Type: application/json. To send another Content-Type, set it in headers (or the subscription's headers). Must not be empty; defaults to application/json."
          type: string
        expected_status_codes:
          description: HTTP status codes treated as a successful delivery, each between 100 and 599. Defaults to 200, 201, 202 and 204.
          items:
            format: int64
            type: integer
          type: array
        follow_redirects:
          description: "Whether to follow HTTP redirects returned by the endpoint. Each redirect target passes the same SSRF checks, and a redirect that changes the scheme, host or port gets only Sparrow's own headers: headers and secret_headers are dropped. Defaults to true."
          type: boolean
        max_retries:
          description: Retries after the first attempt before a delivery is marked failed, so a delivery gets up to max_retries + 1 attempts. 0 means no retries. Between 0 and 20; defaults to 13, which with the default retry_backoff_seconds keeps retrying for about three days.
          format: int64
          type: integer
        rate_limit_rps:
          description: Maximum delivery rate to this webhook, in requests per second (fractions allowed, e.g. 0.5). Must be positive; unset means no limit. Deliveries over the limit wait in the queue.
          format: double
          type: number
        request_timeout_seconds:
          description: How long to wait for the endpoint to respond before treating the attempt as a timeout, in seconds. Between 1 and 300; defaults to 30.
          format: int64
          type: integer
        retry_backoff_seconds:
          description: Base retry delay, in seconds. The delay after attempt N is retry_backoff_seconds * 2^(N-1), capped at 24 hours, minus a random 0-20% so retries spread out. Between 1 and 3600; defaults to 60. The defaults give retries after about 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 and 1024 minutes, then 24 hours twice.
          format: int64
          type: integer
        user_agent:
          description: "Stored and returned, but deliveries do not use it yet: they always send User-Agent: Sparrow-Webhook/<server version>. To send another User-Agent, set it in headers. Defaults to Sparrow-Webhook/1.0."
          type: string
        verify_ssl:
          description: Whether to verify the endpoint's TLS certificate. Defaults to true. Set false only for trusted internal endpoints with self-signed certificates; SSRF and redirect checks still apply.
          type: boolean
      type: object
    WebhookHTTPConfigOut:
      additionalProperties: false
      properties:
        capture_response_body:
          description: "false: up to 1 KiB of each response body is stored; true: up to SPARROW_MAX_CAPTURED_RESPONSE_BYTES (default 1 MiB)."
          type: boolean
        content_type:
          description: "Stored value; deliveries currently send Content-Type: application/json unless headers override it."
          type: string
        expected_status_codes:
          description: HTTP status codes treated as a successful delivery.
          items:
            format: int32
            type: integer
          type: array
        follow_redirects:
          description: Whether HTTP redirects returned by the endpoint are followed.
          type: boolean
        max_retries:
          description: Retries after the first attempt before a delivery is marked failed (up to max_retries + 1 attempts).
          format: int64
          type: integer
        rate_limit_rps:
          description: Delivery rate limit for this webhook, in requests per second; absent means no limit.
          format: double
          type: number
        request_timeout_seconds:
          description: Per-attempt request timeout, in seconds.
          format: int64
          type: integer
        retry_backoff_seconds:
          description: Base retry delay, in seconds; the delay after attempt N is retry_backoff_seconds * 2^(N-1), capped at 24 hours, minus a random 0-20%.
          format: int64
          type: integer
        user_agent:
          description: "Stored value; deliveries currently always send User-Agent: Sparrow-Webhook/<server version> unless headers override it."
          type: string
        verify_ssl:
          description: Whether the endpoint's TLS certificate is verified.
          type: boolean
        webhook_secret:
          description: HMAC signing secret. Shown in full only once, in the response to webhook registration; masked on every later read.
          type: string
      required:
        - max_retries
        - retry_backoff_seconds
        - capture_response_body
        - follow_redirects
        - verify_ssl
        - request_timeout_seconds
      type: object
    WebhookHealthOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/WebhookHealthOutputBody.json
          format: uri
          readOnly: true
          type: string
        avg_response_time:
          description: Average endpoint response time over the window, in milliseconds.
          format: int64
          type: integer
        client_errors:
          description: Failed attempts in the window with a 4xx response (client_error).
          format: int64
          type: integer
        consecutive_failures:
          description: Current run of consecutive failed attempts; resets on a success.
          format: int64
          type: integer
        failed_deliveries:
          description: Failed attempts in the window.
          format: int64
          type: integer
        health:
          description: "Health label from the last health evaluation: healthy, degraded, unhealthy, or unknown (too few recent attempts). Updated every SPARROW_HEALTH_EVAL_INTERVAL (default 1m)."
          enum:
            - healthy
            - degraded
            - unhealthy
            - unknown
          type: string
        network_errors:
          description: Failed attempts in the window with a network-level error (DNS, TLS, connection refused, reset).
          format: int64
          type: integer
        rules:
          $ref: "#/components/schemas/HealthRules"
          description: Thresholds the health status is derived from.
        server_errors:
          description: Failed attempts in the window with a 5xx response (server_error).
          format: int64
          type: integer
        success_rate:
          description: successful_deliveries / total_deliveries over the window, 0.0 to 1.0.
          format: double
          type: number
        successful_deliveries:
          description: Successful attempts in the window.
          format: int64
          type: integer
        timeout_errors:
          description: Failed attempts in the window that timed out.
          format: int64
          type: integer
        total_deliveries:
          description: Delivery attempts in the last rules.window_hours hours (each retry counts).
          format: int64
          type: integer
        unexpected_status_errors:
          description: Attempts in the window whose 2xx/3xx response was not in expected_status_codes.
          format: int64
          type: integer
        webhook_id:
          description: Webhook id (UUID).
          type: string
      required:
        - webhook_id
        - health
        - total_deliveries
        - successful_deliveries
        - failed_deliveries
        - consecutive_failures
        - success_rate
        - avg_response_time
        - client_errors
        - server_errors
        - timeout_errors
        - network_errors
        - unexpected_status_errors
        - rules
      type: object
    WebhookOut:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/WebhookOut.json
          format: uri
          readOnly: true
          type: string
        active:
          description: "Whether deliveries are sent. false: paused by an operator or auto-disabled; new deliveries are held as paused."
          type: boolean
        auto_disabled_at:
          description: Set when Sparrow paused this webhook itself because its receiver kept failing (see SPARROW_AUTO_DISABLE_AFTER), RFC3339. Cleared when the webhook is resumed.
          format: date-time
          type: string
        auto_disabled_reason:
          description: Why Sparrow auto-disabled this webhook. Cleared when the webhook is resumed.
          type: string
        consumer:
          description: Consumer this webhook belongs to.
          type: string
        created_at:
          description: Creation timestamp, RFC3339.
          format: date-time
          type: string
        description:
          description: Human-readable note about this webhook.
          type: string
        events:
          description: Event type names this webhook is currently subscribed to, derived from its subscriptions.
          items:
            type: string
          type: array
        headers:
          additionalProperties:
            type: string
          description: Static HTTP headers sent with every delivery.
          type: object
        health:
          description: Health label from the last health evaluation (see getWebhookHealth).
          enum:
            - healthy
            - degraded
            - unhealthy
            - unknown
          type: string
        http_config:
          $ref: "#/components/schemas/WebhookHTTPConfigOut"
          description: Per-webhook HTTP delivery configuration.
        requires_transform:
          description: Whether every subscription must carry an enabled transform_template because the receiver only accepts transformed payloads.
          type: boolean
        secret_headers:
          additionalProperties:
            type: string
          description: Encrypted secret header names, with values always masked. Update them via PATCH secret_headers; send an empty string to remove one.
          type: object
        signature_type:
          description: Signing scheme. "hmac" (default) signs every delivery with HMAC-SHA256 (v1,); "ed25519" adds an Ed25519 signature (v1a,) alongside the HMAC one.
          enum:
            - hmac
            - ed25519
          type: string
        signing_public_key:
          description: Hex-encoded Ed25519 public key for verifying the v1a, delivery signature. Safe to expose; there is no private-key equivalent to mask.
          type: string
        updated_at:
          description: Last-modified timestamp, RFC3339.
          format: date-time
          type: string
        url:
          description: HTTP endpoint deliveries are POSTed to.
          type: string
        webhook_id:
          description: Webhook id (UUID).
          type: string
      required:
        - webhook_id
        - consumer
        - events
        - url
        - active
        - health
        - signature_type
        - http_config
        - requires_transform
        - created_at
        - updated_at
      type: object
    WhoAmIOutputBody:
      additionalProperties: false
      properties:
        $schema:
          description: A URL to the JSON Schema for this object.
          examples:
            - https://example.com/schemas/WhoAmIOutputBody.json
          format: uri
          readOnly: true
          type: string
        auth_enabled:
          description: "False when SPARROW_API_KEY is unset: every endpoint is open."
          type: boolean
        master_key:
          description: True when the request used SPARROW_API_KEY itself. Always false when auth is off.
          type: boolean
        name:
          description: "Name of the credential used for this request: the master key's name, the access token's name, or a placeholder when auth is off."
          type: string
        token_id:
          description: Id of the access token used; null for the master key or when auth is off.
          type:
            - string
            - "null"
      required:
        - auth_enabled
        - name
        - master_key
        - token_id
      type: object
  securitySchemes:
    ApiKeyAuth:
      description: "The master key (SPARROW_API_KEY) or a tenant-wide access token. Also accepted as Authorization: Bearer <credential>. Required only when the server is started with SPARROW_API_KEY set; otherwise all endpoints are open. The embedded UI never receives the key: it asks for a credential on the first 401."
      in: header
      name: X-API-Key
      type: apiKey
info:
  contact:
    name: Sparrow
    url: https://github.com/sarathsp06/sparrow
  description: |-
    Sparrow is a self-hosted webhook delivery platform: register the events your system produces, register the webhooks that should receive them, then push event occurrences and Sparrow fans them out with retries and health tracking.

    ## Concepts

    - **Event type** (`Event Types`) — a named, versioned schema for something that happened in your system, e.g. `order.created`. Optional JSON Schema validation is *soft*: invalid payloads are stored with warnings, never rejected.
    - **Event** (`Events`) — one occurrence of an event type, pushed with a payload. Pushing an event asynchronously fans it out to every matching subscription.
    - **Webhook** (`Webhooks`) — a registered HTTP endpoint plus its delivery configuration (retries, timeouts, signing). Every delivery is signed with HMAC-SHA256 (Standard Webhooks), plus Ed25519 when the webhook's signature_type is ed25519.
    - **Subscription** (`Subscriptions`) — links one webhook to one event type within a consumer, optionally with a Go-template payload transform and label filters. Registering a webhook auto-creates matching subscriptions.
    - **Delivery** (`Deliveries`) — one attempt (and its retry history) to send an event to a webhook. Failures are classified as retryable (5xx, timeout, network) or not (4xx, DNS, TLS).
    - **Health** (`Health`) — a per-webhook label (healthy/degraded/unhealthy/unknown) derived from the last 24 hours of attempts, refreshed every `SPARROW_HEALTH_EVAL_INTERVAL`.

    ## Consumers

    Webhooks, events, subscriptions, and deliveries are scoped to a `consumer` path segment (defaults to `default` if you don't need multi-tenancy). A handful of read endpoints are deliberately global (no consumer) for cross-tenant dashboards and id-only lookups where the resource id is already globally unique.

    ## Authentication

    Optional. When the server is started with `SPARROW_API_KEY` set, every `/v1/*` request must carry that master key or a tenant-wide access token (`createToken` without a `consumer`), in the `X-API-Key` header or as `Authorization: Bearer <credential>`. When `SPARROW_API_KEY` is unset, every endpoint is open, and outside `SPARROW_ENVIRONMENT=production` CORS also allows any origin: never expose such a server beyond a trusted network.

    ## Portal tokens

    A consumer-scoped access token (`createToken` with a `consumer`) powers the embedded portal at `/portal`. It is refused on `/v1` (403); its API calls go to the single `/portal/api/` prefix with `Authorization: Bearer <token>`, and the consumer is carried by the token, not the URL, so exposing the portal means allowlisting only `/portal`, `/_app`, and `/portal/api`. `/portal/api/<rest>` is served by `/v1/consumers/<token's consumer>/<rest>`, except pushing or re-pushing events (`POST events`, `POST events:rePush`, `repush-jobs`, `prepare_repush`: refused), plus these global helpers: `GET event-types[/...]` (listed for the token's consumer), `GET template-functions`, `GET capabilities`, and `POST subscriptions:testTemplate`. With `SPARROW_PORTAL_RETRY=false` the delivery retry calls and `prepare_retry` are refused too (403), and `GET capabilities` reports `portal.retry: false`. Each operation's description says whether portal tokens can call it.

    ## Errors

    Errors from the API itself are RFC 9457 problem details (`Content-Type: application/problem+json`): `{"title", "status", "detail", "errors"}`, where `errors` lists per-field validation problems. Two layers in front of the API answer with plain `application/json` instead:

    - Authentication (`401`, `503`): `{"error": "unauthorized", "reason": "missing|invalid|expired|revoked", "message": "..."}`; `reason` is absent for `503 unavailable` (retry after the `Retry-After` header).
    - Authorization and the portal gateway (`401`, `403`): `{"error": "forbidden" or "unauthorized", "message": "..."}`, e.g. a consumer-scoped token on `/v1`.

    Status codes: `400` invalid input, `404` not found, `409` conflict or wrong state, `413` body over `SPARROW_MAX_BODY_BYTES`, `429` rate limited, `500` internal (details are logged, not returned), `503` temporarily unavailable, `504` timed out.
  license:
    identifier: MIT
    name: MIT
  title: Sparrow
  version: 1.0.0
openapi: 3.1.0
paths:
  /v1/capabilities:
    get:
      description: |-
        Reports which optional, deployment-configured features this server offers so clients can show or hide the matching UI: AI-assisted transform template drafting, with the provider and model in use, whether alert email delivery is set up, and whether portal tokens may retry deliveries. Read-only.

        ai_drafting.enabled reflects SPARROW_AI_ENABLED=true; drafting is usable only when enabled is true and problem is absent; prompt_only is the opposite of enabled.

        Portal tokens: allowed, as GET /portal/api/capabilities.
      operationId: getCapabilities
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapabilitiesOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: List optional server features
      tags:
        - Server
  /v1/consumers:
    get:
      description: |-
        Lists the consumers that own a webhook or an event, sorted by name, optionally only those whose name contains q (case-insensitive). Consumers are created implicitly by registering a webhook or pushing an event under them; this is how clients find them, e.g. for a consumer picker. Not paginated: returns at most limit names.

        Portal tokens: not allowed.
      operationId: listConsumers
      parameters:
        - description: Only consumers whose name contains this text, case-insensitive.
          explode: false
          in: query
          name: q
          schema:
            description: Only consumers whose name contains this text, case-insensitive.
            maxLength: 255
            type: string
        - description: Maximum names to return.
          explode: false
          in: query
          name: limit
          schema:
            default: 50
            description: Maximum names to return.
            format: int32
            maximum: 1000
            minimum: 1
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListConsumersOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: Search consumer names
      tags:
        - Webhooks
  /v1/consumers/{consumer}/alert-configs:
    get:
      description: |-
        Lists alert configs in a consumer, oldest first, optionally filtered to one webhook's own configs. Not paginated.

        Portal tokens: allowed, as GET /portal/api/alert-configs.
      operationId: listAlertConfigs
      parameters:
        - description: Consumer to list alert configs in.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer to list alert configs in.
            type: string
        - description: Filter to alert configs scoped to one webhook. Omit to list every config in the consumer, including consumer-wide ones.
          explode: false
          in: query
          name: webhook_id
          schema:
            description: Filter to alert configs scoped to one webhook. Omit to list every config in the consumer, including consumer-wide ones.
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListAlertConfigsOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: List email alert recipients
      tags:
        - Alert Configs
    post:
      description: |-
        Opts an email address into Sparrow's self-generated health-change, permanent-delivery-failure and auto-disable notifications, scoped to one webhook or the whole consumer. Emails are only sent once an alert mailer webhook is registered under the _sparrow consumer (see GET /v1/capabilities alert_delivery.configured). Not idempotent: each call adds a config.

        400 for an invalid email or event type, 404 when webhook_id is not in this consumer.

        Portal tokens: allowed, as POST /portal/api/alert-configs.
      operationId: createAlertConfig
      parameters:
        - description: Consumer whose webhooks the alert covers.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer whose webhooks the alert covers.
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateAlertConfigBody"
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AlertConfigItem"
          description: Created
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Register an email alert recipient
      tags:
        - Alert Configs
  /v1/consumers/{consumer}/alert-configs/{alert_config_id}:
    delete:
      description: |-
        Removes an alert config. The webhook and its delivery history are unaffected. Returns 204; 404 when it is not in this consumer.

        Portal tokens: allowed, as DELETE /portal/api/alert-configs/{alert_config_id}.
      operationId: deleteAlertConfig
      parameters:
        - description: Consumer the alert config belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer the alert config belongs to.
            type: string
        - description: Alert config id (UUID).
          in: path
          name: alert_config_id
          required: true
          schema:
            description: Alert config id (UUID).
            type: string
      responses:
        "204":
          description: No Content
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Delete an email alert recipient
      tags:
        - Alert Configs
  /v1/consumers/{consumer}/deliveries:
    get:
      description: |-
        Lists deliveries in a consumer, newest first, optionally filtered by webhook, event occurrence, subscription, event type, status, error category, or creation time. Cursor paginated: pass pagination.next_cursor as cursor for the next page; there is no total count and offset is rejected.

        Side effect: prepare_retry=true also snapshots every match (up to 10,000) into a batch job and returns retry_id for POST .../deliveries:retryBatch.

        Portal tokens: allowed, as GET /portal/api/deliveries; prepare_retry only while SPARROW_PORTAL_RETRY is true (otherwise 403).
      operationId: listDeliveries
      parameters:
        - description: Consumer to list deliveries in.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer to list deliveries in.
            type: string
        - description: Filter to deliveries for one webhook (UUID; 400 otherwise).
          explode: false
          in: query
          name: webhook_id
          schema:
            description: Filter to deliveries for one webhook (UUID; 400 otherwise).
            type: string
        - description: Filter to deliveries for one pushed event occurrence (UUID; 400 otherwise).
          explode: false
          in: query
          name: event_id
          schema:
            description: Filter to deliveries for one pushed event occurrence (UUID; 400 otherwise).
            type: string
        - description: Filter by delivery status.
          explode: false
          in: query
          name: status
          schema:
            description: Filter by delivery status.
            enum:
              - pending
              - sending
              - success
              - failed
              - retrying
              - expired
              - paused
            type: string
        - description: Filter by the most recent attempt's error_category, e.g. server_error, client_error, timeout.
          explode: false
          in: query
          name: error_category
          schema:
            description: Filter by the most recent attempt's error_category, e.g. server_error, client_error, timeout.
            type: string
        - description: Filter to deliveries created by one subscription (UUID), e.g. its paused deliveries.
          explode: false
          in: query
          name: subscription_id
          schema:
            description: Filter to deliveries created by one subscription (UUID), e.g. its paused deliveries.
            type: string
        - description: Filter to deliveries of one event type, e.g. order.created. Also matches deliveries whose subscription has since been replaced or deleted.
          explode: false
          in: query
          name: event_name
          schema:
            description: Filter to deliveries of one event type, e.g. order.created. Also matches deliveries whose subscription has since been replaced or deleted.
            type: string
        - description: "Only deliveries created at or after this time: YYYY-MM-DD (start of that day, UTC) or an RFC3339 timestamp (e.g. an import's imported_at)."
          explode: false
          in: query
          name: created_after
          schema:
            description: "Only deliveries created at or after this time: YYYY-MM-DD (start of that day, UTC) or an RFC3339 timestamp (e.g. an import's imported_at)."
            type: string
        - description: "Only deliveries created at or before this time: YYYY-MM-DD (through the end of that day, UTC) or an RFC3339 timestamp."
          explode: false
          in: query
          name: created_before
          schema:
            description: "Only deliveries created at or before this time: YYYY-MM-DD (through the end of that day, UTC) or an RFC3339 timestamp."
            type: string
        - description: "Side effect: when true, also snapshots the ids of every delivery matching the filters (not just this page; at most 10,000, more is an error) into a batch job and returns its id as retry_id. Pass it to POST .../deliveries:retryBatch within 15 minutes. Each call creates a new snapshot."
          explode: false
          in: query
          name: prepare_retry
          schema:
            default: false
            description: "Side effect: when true, also snapshots the ids of every delivery matching the filters (not just this page; at most 10,000, more is an error) into a batch job and returns its id as retry_id. Pass it to POST .../deliveries:retryBatch within 15 minutes. Each call creates a new snapshot."
            type: boolean
        - description: Maximum items to return, 1 to 1000.
          explode: false
          in: query
          name: limit
          schema:
            default: 50
            description: Maximum items to return, 1 to 1000.
            format: int32
            maximum: 1000
            minimum: 1
            type: integer
        - description: "Continue after the page this came from: pagination.next_cursor of the previous response. Stable while new deliveries arrive."
          explode: false
          in: query
          name: cursor
          schema:
            description: "Continue after the page this came from: pagination.next_cursor of the previous response. Stable while new deliveries arrive."
            type: string
        - description: "Removed: page with cursor. Any value other than 0 is rejected with 400, so a client still paging by offset fails loudly instead of re-reading the first page."
          explode: false
          in: query
          name: offset
          schema:
            default: 0
            description: "Removed: page with cursor. Any value other than 0 is rejected with 400, so a client still paging by offset fails loudly instead of re-reading the first page."
            format: int32
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListDeliveriesOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: List deliveries
      tags:
        - Deliveries
  /v1/consumers/{consumer}/deliveries/{delivery_id}:
    get:
      description: |-
        Returns one delivery's status, latest response code/body, and error classification. 404 when it does not exist in this consumer.

        Portal tokens: allowed, as GET /portal/api/deliveries/{delivery_id}.
      operationId: getDelivery
      parameters:
        - description: Consumer the delivery belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer the delivery belongs to.
            type: string
        - description: Delivery id (UUID).
          in: path
          name: delivery_id
          required: true
          schema:
            description: Delivery id (UUID).
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeliveryItem"
          description: OK
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Get a delivery by id
      tags:
        - Deliveries
  /v1/consumers/{consumer}/deliveries/{delivery_id}/attempts:
    get:
      description: |-
        Returns the per-attempt record for one delivery, oldest first: response code, timing, and error classification for every attempt made so far, across manual retries. A delivery that does not exist or belongs to another consumer answers 404.

        Portal tokens: allowed, as GET /portal/api/deliveries/{delivery_id}/attempts.
      operationId: getDeliveryAttempts
      parameters:
        - description: Consumer the delivery belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer the delivery belongs to.
            type: string
        - description: Delivery id (UUID).
          in: path
          name: delivery_id
          required: true
          schema:
            description: Delivery id (UUID).
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AttemptsOutputBody"
          description: OK
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Get a delivery's per-attempt history
      tags:
        - Deliveries
  /v1/consumers/{consumer}/deliveries/{delivery_id}:retry:
    post:
      description: |-
        Queues one delivery for another round of attempts right away: it is reset to pending and gets up to max_attempts new attempts, without TTL expiry. Works for failed, expired, paused and retrying deliveries: a retrying delivery's scheduled retry is cancelled and replaced, so it is sent now rather than after its backoff. Refused with 409 for a successful delivery (use the bulk route with force to resend it) and for one that is pending or being sent right now (retry it once that attempt finishes). If the webhook or subscription is still paused the delivery is held as paused again. Not idempotent: each call queues another round.

        Portal tokens: allowed, as POST /portal/api/deliveries/{delivery_id}:retry, unless the server runs with SPARROW_PORTAL_RETRY=false (then 403).
      operationId: retryDelivery
      parameters:
        - description: Consumer the delivery belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer the delivery belongs to.
            type: string
        - description: Delivery id (UUID) to retry.
          in: path
          name: delivery_id
          required: true
          schema:
            description: Delivery id (UUID) to retry.
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RetryOutputBody"
          description: OK
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "409":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Conflict
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Retry a single delivery
      tags:
        - Deliveries
  /v1/consumers/{consumer}/deliveries:retry:
    post:
      description: |-
        Queues every failed or paused delivery of one webhook for another round of attempts (see retryDelivery), in one request. With force, every delivery of the webhook is resent, including successful, expired and retrying ones (a retrying one's scheduled retry is replaced); deliveries pending or being sent right now are skipped. Returns count 0 when nothing matched. For a filtered or very large set, use prepare_retry and :retryBatch instead.

        404 when the webhook does not exist in this consumer.

        Portal tokens: allowed, as POST /portal/api/deliveries:retry, unless the server runs with SPARROW_PORTAL_RETRY=false (then 403).
      operationId: retryDeliveriesByWebhook
      parameters:
        - description: Consumer the webhook belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer the webhook belongs to.
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RetryDeliveriesByWebhookInputBody"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RetryOutputBody"
          description: OK
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Retry deliveries in bulk for a webhook
      tags:
        - Deliveries
  /v1/consumers/{consumer}/deliveries:retryBatch:
    post:
      description: |-
        Starts an async job that retries every delivery in a snapshot taken by an earlier GET .../deliveries?prepare_retry=true call, like retryDelivery for each one. Send the retry_id the deliveries list returned. Returns 202 with the job; poll it with getDeliveryRetryJob.

        A snapshot can be started once: 409 when it is not pending (already started or cancelled), expired (15 minutes after it was taken), or is an event re-push snapshot; 404 when the id is unknown, 400 when it is not a UUID.

        Portal tokens: allowed, as POST /portal/api/deliveries:retryBatch, unless the server runs with SPARROW_PORTAL_RETRY=false (then 403).
      operationId: startDeliveryRetryJob
      parameters:
        - description: Consumer the job belongs to. A job snapshotted for another consumer answers 404; a job snapshotted across all consumers is reachable under any consumer, except with a portal token.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer the job belongs to. A job snapshotted for another consumer answers 404; a job snapshotted across all consumers is reachable under any consumer, except with a portal token.
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RetryBatchInputBody"
        required: true
      responses:
        "202":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchJobOutputBody"
          description: Accepted
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "409":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Conflict
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Start a batch retry job from a prepared snapshot
      tags:
        - Deliveries
  /v1/consumers/{consumer}/events:
    get:
      description: |-
        Lists pushed event occurrences in a consumer, newest first, with delivery outcome counts per occurrence. Cursor paginated: pass pagination.next_cursor as cursor for the next page; there is no total count and offset is rejected.

        Side effect: prepare_repush=true also snapshots every match (up to 10,000) into a batch job and returns repush_id for POST .../events:rePush.

        Portal tokens: allowed, as GET /portal/api/events, without prepare_repush (re-push is not a portal action; 403).
      operationId: listEventOccurrences
      parameters:
        - description: Consumer to list occurrences in.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer to list occurrences in.
            type: string
        - description: Filter to occurrences of this event type name.
          explode: false
          in: query
          name: event
          schema:
            description: Filter to occurrences of this event type name.
            type: string
        - description: "Filter by schema validation result: valid or invalid. Other values are 400."
          explode: false
          in: query
          name: schema_valid
          schema:
            description: "Filter by schema validation result: valid or invalid. Other values are 400."
            type: string
        - description: Filter by labels as comma-separated key=value pairs, e.g. region=eu,tier=gold. Matches occurrences whose labels contain every pair.
          explode: false
          in: query
          name: labels
          schema:
            description: Filter by labels as comma-separated key=value pairs, e.g. region=eu,tier=gold. Matches occurrences whose labels contain every pair.
            type: string
        - description: "Only occurrences created at or after this time: YYYY-MM-DD (start of that day, UTC) or an RFC3339 timestamp."
          explode: false
          in: query
          name: created_after
          schema:
            description: "Only occurrences created at or after this time: YYYY-MM-DD (start of that day, UTC) or an RFC3339 timestamp."
            type: string
        - description: "Only occurrences created at or before this time: YYYY-MM-DD (through the end of that day, UTC) or an RFC3339 timestamp."
          explode: false
          in: query
          name: created_before
          schema:
            description: "Only occurrences created at or before this time: YYYY-MM-DD (through the end of that day, UTC) or an RFC3339 timestamp."
            type: string
        - description: "Side effect: when true, also snapshots the ids of every occurrence matching the filters (not just this page; at most 10,000, more is an error) into a batch job and returns its id as repush_id. Pass it to POST .../events:rePush within 15 minutes. Each call creates a new snapshot."
          explode: false
          in: query
          name: prepare_repush
          schema:
            default: false
            description: "Side effect: when true, also snapshots the ids of every occurrence matching the filters (not just this page; at most 10,000, more is an error) into a batch job and returns its id as repush_id. Pass it to POST .../events:rePush within 15 minutes. Each call creates a new snapshot."
            type: boolean
        - description: Maximum items to return, 1 to 1000.
          explode: false
          in: query
          name: limit
          schema:
            default: 50
            description: Maximum items to return, 1 to 1000.
            format: int32
            maximum: 1000
            minimum: 1
            type: integer
        - description: "Continue after the page this came from: pagination.next_cursor of the previous response. Stable while new events arrive."
          explode: false
          in: query
          name: cursor
          schema:
            description: "Continue after the page this came from: pagination.next_cursor of the previous response. Stable while new events arrive."
            type: string
        - description: "Removed: page with cursor. Any value other than 0 is rejected with 400, so a client still paging by offset fails loudly instead of re-reading the first page."
          explode: false
          in: query
          name: offset
          schema:
            default: 0
            description: "Removed: page with cursor. Any value other than 0 is rejected with 400, so a client still paging by offset fails loudly instead of re-reading the first page."
            format: int32
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListEventOccurrencesOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: List pushed event occurrences
      tags:
        - Events
    post:
      description: |-
        Records one occurrence of a registered event type and asynchronously fans it out to every subscription whose event name and label filters match. Returns 201 as soon as the event is stored and queued; delivery happens in the background, so check Deliveries for outcomes. A payload that fails the event type's JSON Schema is still accepted (schema_valid: false, with warnings).

        Idempotency: with idempotency_key, a repeat push with the same key in the same consumer returns 201 with duplicate: true and the original event_id, and creates nothing. Without a key every call creates a new event.

        Scheduling: with scheduled_at, the occurrence is stored immediately but fans out only at that time; until then it lists with no deliveries. A past time means immediate; more than one year ahead is 400.

        Failures: 404 for an unregistered event type (unless SPARROW_AUTO_REGISTER_EVENTS=true), 409 for an inactive one, 400 for a reserved sparrow.* name, a name over 255 characters, invalid labels or a scheduled_at over a year ahead, 413 for a body over SPARROW_MAX_BODY_BYTES.

        Portal tokens: not allowed (pushing is the producer's job).
      operationId: pushEvent
      parameters:
        - description: Consumer to record the occurrence in. Created implicitly on first use.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer to record the occurrence in. Created implicitly on first use.
            type: string
        - description: Name of a registered, active event type (at most 255 characters). An unregistered name is 404 unless SPARROW_AUTO_REGISTER_EVENTS=true, which registers it if it follows the event type naming rule (lowercase letters, digits, '_' and '-' in dot-separated parts; 400 otherwise); sparrow.* names are reserved (400).
          explode: false
          in: query
          name: event
          required: true
          schema:
            description: Name of a registered, active event type (at most 255 characters). An unregistered name is 404 unless SPARROW_AUTO_REGISTER_EVENTS=true, which registers it if it follows the event type naming rule (lowercase letters, digits, '_' and '-' in dot-separated parts; 400 otherwise); sparrow.* names are reserved (400).
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PushEventBody"
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PushEventOutputBody"
          description: Created
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "409":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Conflict
        "413":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Request Entity Too Large
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Push an event occurrence for asynchronous delivery
      tags:
        - Events
  /v1/consumers/{consumer}/events:rePush:
    post:
      description: |-
        Starts an async job that re-pushes every event occurrence in a snapshot taken by an earlier GET .../events?prepare_repush=true call. Each re-push creates a new event occurrence with the original payload, without re-validating it; sparrow.* system events are skipped and counted as failed. Returns 202 with the job; poll it with getEventRepushJob.

        A snapshot can be started once: 409 when it is not pending (already started or cancelled) or expired (15 minutes after it was taken), 404 when the id is unknown, 400 when it is not a UUID.

        Portal tokens: not allowed (re-pushing events is the producer's job, like pushing them).
      operationId: startEventRepushJob
      parameters:
        - description: Consumer the job belongs to. A job snapshotted for another consumer answers 404; a job snapshotted across all consumers is reachable under any consumer, except with a portal token.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer the job belongs to. A job snapshotted for another consumer answers 404; a job snapshotted across all consumers is reachable under any consumer, except with a portal token.
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RepushBatchInputBody"
        required: true
      responses:
        "202":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchJobOutputBody"
          description: Accepted
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "409":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Conflict
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Start a batch re-push job from a prepared snapshot
      tags:
        - Events
  /v1/consumers/{consumer}/repush-jobs/{job_id}:
    get:
      description: |-
        Returns a batch re-push job's status and processed/failed/total counts. 404 when the id is unknown, 409 when the id belongs to a delivery retry job.

        Portal tokens: not allowed.
      operationId: getEventRepushJob
      parameters:
        - description: Consumer the job belongs to. A job snapshotted for another consumer answers 404; a job snapshotted across all consumers is reachable under any consumer, except with a portal token.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer the job belongs to. A job snapshotted for another consumer answers 404; a job snapshotted across all consumers is reachable under any consumer, except with a portal token.
            type: string
        - description: "Batch job id (UUID): the repush_id or retry_id from the list call."
          in: path
          name: job_id
          required: true
          schema:
            description: "Batch job id (UUID): the repush_id or retry_id from the list call."
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchJobOutputBody"
          description: OK
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "409":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Conflict
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Get batch re-push job progress
      tags:
        - Events
  /v1/consumers/{consumer}/repush-jobs/{job_id}:cancel:
    post:
      description: |-
        Cancels a pending or running batch re-push job; a running job stops at its next progress check (every 25 items). Occurrences already re-pushed are not rolled back. Returns 204. 409 when the job is already completed or cancelled.

        Portal tokens: not allowed.
      operationId: cancelEventRepushJob
      parameters:
        - description: Consumer the job belongs to. A job snapshotted for another consumer answers 404; a job snapshotted across all consumers is reachable under any consumer, except with a portal token.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer the job belongs to. A job snapshotted for another consumer answers 404; a job snapshotted across all consumers is reachable under any consumer, except with a portal token.
            type: string
        - description: "Batch job id (UUID): the repush_id or retry_id from the list call."
          in: path
          name: job_id
          required: true
          schema:
            description: "Batch job id (UUID): the repush_id or retry_id from the list call."
            type: string
      responses:
        "204":
          description: No Content
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "409":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Conflict
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Cancel a pending or in-progress batch re-push job
      tags:
        - Events
  /v1/consumers/{consumer}/retry-jobs/{job_id}:
    get:
      description: |-
        Returns a batch retry job's status and processed/failed/total counts. 404 when the id is unknown, 409 when the id belongs to an event re-push job.

        Portal tokens: allowed, as GET /portal/api/retry-jobs/{job_id}.
      operationId: getDeliveryRetryJob
      parameters:
        - description: Consumer the job belongs to. A job snapshotted for another consumer answers 404; a job snapshotted across all consumers is reachable under any consumer, except with a portal token.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer the job belongs to. A job snapshotted for another consumer answers 404; a job snapshotted across all consumers is reachable under any consumer, except with a portal token.
            type: string
        - description: "Batch job id (UUID): the repush_id or retry_id from the list call."
          in: path
          name: job_id
          required: true
          schema:
            description: "Batch job id (UUID): the repush_id or retry_id from the list call."
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchJobOutputBody"
          description: OK
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "409":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Conflict
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Get batch retry job progress
      tags:
        - Deliveries
  /v1/consumers/{consumer}/retry-jobs/{job_id}:cancel:
    post:
      description: |-
        Cancels a pending or running batch retry job; a running job stops at its next progress check (every 25 items). Deliveries already queued are not rolled back. Returns 204. 409 when the job is already completed or cancelled.

        Portal tokens: allowed, as POST /portal/api/retry-jobs/{job_id}:cancel.
      operationId: cancelDeliveryRetryJob
      parameters:
        - description: Consumer the job belongs to. A job snapshotted for another consumer answers 404; a job snapshotted across all consumers is reachable under any consumer, except with a portal token.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer the job belongs to. A job snapshotted for another consumer answers 404; a job snapshotted across all consumers is reachable under any consumer, except with a portal token.
            type: string
        - description: "Batch job id (UUID): the repush_id or retry_id from the list call."
          in: path
          name: job_id
          required: true
          schema:
            description: "Batch job id (UUID): the repush_id or retry_id from the list call."
            type: string
      responses:
        "204":
          description: No Content
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "409":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Conflict
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Cancel a pending or in-progress batch retry job
      tags:
        - Deliveries
  /v1/consumers/{consumer}/stats:
    get:
      description: |-
        Returns webhook and delivery-attempt counts (total, active, successful, failed, pending, success rate) for one consumer. Attempt counts come from the health evaluator and lag by up to SPARROW_HEALTH_EVAL_INTERVAL; pending is live.

        Portal tokens: allowed, as GET /portal/api/stats.
      operationId: getConsumerStats
      parameters:
        - description: Consumer name.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer name.
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConsumerStatsOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: Get aggregate delivery statistics for a consumer
      tags:
        - Webhooks
  /v1/consumers/{consumer}/subscriptions:
    get:
      description: |-
        Lists subscriptions in a consumer, newest first, optionally filtered by webhook or event type name. Offset paginated with total_count. 404 when webhook_id is given and the webhook is not in this consumer.

        Portal tokens: allowed, as GET /portal/api/subscriptions.
      operationId: listSubscriptions
      parameters:
        - description: Consumer to list subscriptions in.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer to list subscriptions in.
            type: string
        - description: "Filter to subscriptions for one webhook (UUID). Takes precedence over event_name: when both are given, event_name is ignored."
          explode: false
          in: query
          name: webhook_id
          schema:
            description: "Filter to subscriptions for one webhook (UUID). Takes precedence over event_name: when both are given, event_name is ignored."
            type: string
        - description: Filter to subscriptions with exactly this event_name ("*" matches only catch-all subscriptions).
          explode: false
          in: query
          name: event_name
          schema:
            description: Filter to subscriptions with exactly this event_name ("*" matches only catch-all subscriptions).
            type: string
        - description: Maximum items to return, 1 to 1000.
          explode: false
          in: query
          name: limit
          schema:
            default: 50
            description: Maximum items to return, 1 to 1000.
            format: int32
            maximum: 1000
            minimum: 1
            type: integer
        - description: Number of items to skip, for pagination.
          explode: false
          in: query
          name: offset
          schema:
            default: 0
            description: Number of items to skip, for pagination.
            format: int32
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListSubscriptionsOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: List subscriptions
      tags:
        - Subscriptions
    post:
      description: |-
        Subscribes a webhook to an event type within a consumer, with an optional payload transform and label filters. Registering a webhook with events already creates one subscription per name; use this for additional, catch-all ("*") or per-subscription-template subscriptions. A non-empty transform_template is recorded as the first template version.

        Not idempotent: 409 when the webhook already has a subscription for event_name. 404 when the webhook is not in this consumer. 400 for invalid headers, timeout or label filters, a sparrow.* event outside _sparrow, or a missing transform when the webhook has requires_transform.

        Portal tokens: allowed, as POST /portal/api/subscriptions.
      operationId: createSubscription
      parameters:
        - description: Consumer the webhook belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer the webhook belongs to.
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateSubscriptionBody"
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionItem"
          description: Created
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "409":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Conflict
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Create a subscription linking a webhook to an event
      tags:
        - Subscriptions
  /v1/consumers/{consumer}/subscriptions/{subscription_id}:
    delete:
      description: |-
        Deletes the subscription and its template history. The webhook stops receiving that event's occurrences; delivery history is kept. Cannot be undone. A second delete returns 404.

        Portal tokens: allowed, as DELETE /portal/api/subscriptions/{subscription_id}.
      operationId: deleteSubscription
      parameters:
        - description: Consumer the subscription belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer the subscription belongs to.
            type: string
        - description: Subscription id (UUID).
          in: path
          name: subscription_id
          required: true
          schema:
            description: Subscription id (UUID).
            type: string
      responses:
        "204":
          description: No Content
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Delete a subscription
      tags:
        - Subscriptions
    get:
      description: |-
        Returns one subscription: its webhook link, transform template and settings, label filters and pause state. 404 when it is not in this consumer.

        Portal tokens: allowed, as GET /portal/api/subscriptions/{subscription_id}.
      operationId: getSubscription
      parameters:
        - description: Consumer the subscription belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer the subscription belongs to.
            type: string
        - description: Subscription id (UUID).
          in: path
          name: subscription_id
          required: true
          schema:
            description: Subscription id (UUID).
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionItem"
          description: OK
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Get a subscription by id
      tags:
        - Subscriptions
    patch:
      description: |-
        Partially updates a subscription's headers, method, timeout, transform, or label filters: only fields present in the body change, and map fields replace the whole map. The linked webhook_id and event_name cannot be changed; delete and recreate instead. A changed transform_template is recorded as a new template version with template_source and template_notes. Returns the updated subscription. Idempotent: repeating the same body records no new version.

        400 for invalid values, or when the webhook has requires_transform and the result has no enabled template. 404 when it is not in this consumer.

        Portal tokens: allowed, as PATCH /portal/api/subscriptions/{subscription_id}.
      operationId: updateSubscription
      parameters:
        - description: Consumer the subscription belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer the subscription belongs to.
            type: string
        - description: Subscription id (UUID).
          in: path
          name: subscription_id
          required: true
          schema:
            description: Subscription id (UUID).
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PatchSubscriptionBody"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionItem"
          description: OK
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Partially update a subscription
      tags:
        - Subscriptions
  /v1/consumers/{consumer}/subscriptions/{subscription_id}/templateVersions:
    get:
      description: |-
        Returns the transform templates this subscription has been saved with, newest first; the newest is the current one. A version is recorded whenever a save changes the template, with how it was produced (manual or ai_draft), an optional note, and who saved it. The last 20 are kept. There is no restore endpoint: load a version into the editor and save the subscription, which records it as a new version.

        Portal tokens: allowed, as GET /portal/api/subscriptions/{subscription_id}/templateVersions.
      operationId: listSubscriptionTemplateVersions
      parameters:
        - description: Consumer the subscription belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer the subscription belongs to.
            type: string
        - description: Subscription id (UUID).
          in: path
          name: subscription_id
          required: true
          schema:
            description: Subscription id (UUID).
            type: string
        - description: Maximum versions to return, newest first, 1 to 20.
          explode: false
          in: query
          name: limit
          schema:
            default: 20
            description: Maximum versions to return, newest first, 1 to 20.
            format: int64
            maximum: 20
            minimum: 1
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListTemplateVersionsOutputBody"
          description: OK
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: List a subscription's saved template history
      tags:
        - Subscriptions
  /v1/consumers/{consumer}/subscriptions/{subscription_id}:pause:
    post:
      description: |-
        Stops the subscription's deliveries from being attempted, for any reason (receiver maintenance, a template being fixed, an investigation). Events keep fanning out to it: each delivery is recorded with status paused and nothing is queued, so nothing is lost. Queued or retrying deliveries of the subscription are held as paused when their turn comes. A pause never affects the webhook's health. Returns the subscription (200), unlike webhook pause, which returns 204.

        Idempotent: pausing again updates the reason and keeps the original pause time. 400 for a reason over 500 characters, 404 when it is not in this consumer.

        Portal tokens: allowed, as POST /portal/api/subscriptions/{subscription_id}:pause.
      operationId: pauseSubscription
      parameters:
        - description: Consumer the subscription belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer the subscription belongs to.
            type: string
        - description: Subscription id (UUID).
          in: path
          name: subscription_id
          required: true
          schema:
            description: Subscription id (UUID).
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PauseSubscriptionInputBody"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SubscriptionItem"
          description: OK
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Pause a subscription
      tags:
        - Subscriptions
  /v1/consumers/{consumer}/subscriptions/{subscription_id}:resume:
    post:
      description: |-
        Lets new deliveries be attempted again. Deliveries recorded while paused stay paused and are never sent automatically: the response says how many there are and when the pause began, so you can retry all of them or only those since a time with the delivery retry endpoints. Idempotent: resuming a subscription that is not paused succeeds with no paused_since.

        Portal tokens: allowed, as POST /portal/api/subscriptions/{subscription_id}:resume.
      operationId: resumeSubscription
      parameters:
        - description: Consumer the subscription belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer the subscription belongs to.
            type: string
        - description: Subscription id (UUID).
          in: path
          name: subscription_id
          required: true
          schema:
            description: Subscription id (UUID).
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ResumeSubscriptionOutputBody"
          description: OK
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Resume a paused subscription
      tags:
        - Subscriptions
  /v1/consumers/{consumer}/subscriptions/{subscription_id}:sendTestEvent:
    post:
      description: |-
        Sends event_name to the subscription: one signed delivery of payload, or of the event type's sample when payload is omitted, rendered through the subscription's transform as a real delivery would be, to its webhook's URL, synchronously, and returns what the receiver answered. It uses the same request builder, signing, network policy (SSRF protection) and timeout as real deliveries and adds the header X-Sparrow-Test: true. It creates no delivery and never counts toward the webhook's health or auto-disable. A receiver error is reported in the response (status_code, error_class), not as an API error.

        One test event per webhook every 2 seconds on each server: more answer 429. 404 for a subscription not in this consumer. 400 when the subscription does not receive event_name, or when the event type is not registered and no payload is given.

        Portal tokens: allowed, as POST /portal/api/subscriptions/{subscription_id}:sendTestEvent.
      operationId: sendTestEvent
      parameters:
        - description: Tenant consumer the subscription belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Tenant consumer the subscription belongs to.
            type: string
        - description: "Subscription to send the event to: its transform renders the payload, and its webhook receives it."
          in: path
          name: subscription_id
          required: true
          schema:
            description: "Subscription to send the event to: its transform renders the payload, and its webhook receives it."
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SendTestEventInputBody"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SendTestEventOutputBody"
          description: OK
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "429":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Too Many Requests
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
        "503":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Service Unavailable
      summary: Send a test event to a subscription
      tags:
        - Subscriptions
  /v1/consumers/{consumer}/webhooks:
    get:
      description: |-
        Lists the webhooks of one consumer, newest first, optionally filtered by id, subscribed event, active flag, or health status. Secrets are masked. Offset paginated with total_count. Deleted webhooks are never listed.

        Portal tokens: allowed, as GET /portal/api/webhooks.
      operationId: listWebhooks
      parameters:
        - description: Tenant consumer to list webhooks in.
          in: path
          name: consumer
          required: true
          schema:
            description: Tenant consumer to list webhooks in.
            type: string
        - description: Filter to a single webhook by id.
          explode: false
          in: query
          name: webhook_id
          schema:
            description: Filter to a single webhook by id.
            type: string
        - description: Filter to webhooks subscribed to this event type name.
          explode: false
          in: query
          name: event
          schema:
            description: Filter to webhooks subscribed to this event type name.
            type: string
        - description: true returns only active (not paused) webhooks; false returns all.
          explode: false
          in: query
          name: active
          schema:
            default: false
            description: true returns only active (not paused) webhooks; false returns all.
            type: boolean
        - description: Filter by computed health status.
          explode: false
          in: query
          name: health
          schema:
            description: Filter by computed health status.
            enum:
              - healthy
              - degraded
              - unhealthy
              - unknown
            type: string
        - description: Maximum items to return, 1 to 1000.
          explode: false
          in: query
          name: limit
          schema:
            default: 50
            description: Maximum items to return, 1 to 1000.
            format: int32
            maximum: 1000
            minimum: 1
            type: integer
        - description: Number of items to skip, for pagination.
          explode: false
          in: query
          name: offset
          schema:
            default: 0
            description: Number of items to skip, for pagination.
            format: int32
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListWebhooksOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: List webhooks
      tags:
        - Webhooks
    post:
      description: |-
        Registers a new HTTP endpoint and creates one subscription per name in events (with transform_template, when given). Generates the HMAC signing secret and returns it in plaintext in http_config.webhook_secret; this response is the only time it is shown. Event names are not required to be registered event types yet.

        Not idempotent: a second call with the same URL in the same consumer fails with 409 while the first webhook exists. 400 for an invalid URL (scheme, unresolvable or blocked host), invalid headers, http_config out of range, a sparrow.* event outside the _sparrow consumer, or requires_transform without transform_template.

        Portal tokens: allowed, as POST /portal/api/webhooks.
      operationId: registerWebhook
      parameters:
        - description: Consumer to register the webhook under. Created implicitly on first use.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer to register the webhook under. Created implicitly on first use.
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RegisterWebhookBody"
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookOut"
          description: Created
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "409":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Conflict
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Register a webhook
      tags:
        - Webhooks
  /v1/consumers/{consumer}/webhooks/{webhook_id}:
    delete:
      description: |-
        Deletes a webhook (a soft delete): it stops receiving events and no longer appears in webhook lists, and its URL can be registered again in the consumer. Its subscriptions and delivery history are kept, and its deliveries stay listed until retention removes them. Queued deliveries for it fail with "Webhook was deleted". This cannot be undone. A second delete returns 404.

        Portal tokens: allowed, as DELETE /portal/api/webhooks/{webhook_id}.
      operationId: deleteWebhook
      parameters:
        - description: Tenant consumer the webhook belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Tenant consumer the webhook belongs to.
            type: string
        - description: Webhook id (UUID).
          in: path
          name: webhook_id
          required: true
          schema:
            description: Webhook id (UUID).
            type: string
      responses:
        "204":
          description: No Content
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Delete a webhook
      tags:
        - Webhooks
    get:
      description: |-
        Returns one webhook's configuration, subscribed events, masked secrets, and current health label. 404 when it does not exist in this consumer or was deleted.

        Portal tokens: allowed, as GET /portal/api/webhooks/{webhook_id}.
      operationId: getWebhook
      parameters:
        - description: Tenant consumer the webhook belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Tenant consumer the webhook belongs to.
            type: string
        - description: Webhook id (UUID).
          in: path
          name: webhook_id
          required: true
          schema:
            description: Webhook id (UUID).
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookOut"
          description: OK
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Get a webhook by id
      tags:
        - Webhooks
    patch:
      description: |-
        Partially updates a webhook: only fields present in the body change. Within http_config only the sent fields change, and the merged result is validated as on register (400 if out of range). Sending events replaces every subscription of the webhook (see events). Setting active toggles delivery like :pause/:resume but never fails when the state is already as requested. Returns the updated webhook. Idempotent: repeating the same body leaves the same state.

        409 when turning requires_transform on while a subscription has no enabled transform, or replacing events while it is on. The deprecated top-level timeout is applied only when http_config.request_timeout_seconds is not sent.

        Portal tokens: allowed, as PATCH /portal/api/webhooks/{webhook_id}.
      operationId: updateWebhook
      parameters:
        - description: Consumer the webhook belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Consumer the webhook belongs to.
            type: string
        - description: Webhook id (UUID).
          in: path
          name: webhook_id
          required: true
          schema:
            description: Webhook id (UUID).
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PatchWebhookBody"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookOut"
          description: OK
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "409":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Conflict
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Partially update a webhook
      tags:
        - Webhooks
  /v1/consumers/{consumer}/webhooks/{webhook_id}/health:
    get:
      description: |-
        Returns one webhook's health label plus attempt counts and per-category error counts (client, server, timeout, network, unexpected-status) over the last rules.window_hours hours, and the thresholds the label is derived from. The label lags by up to SPARROW_HEALTH_EVAL_INTERVAL. Counts are zero before the first attempt. 404 when the webhook is not in this consumer.

        Portal tokens: allowed, as GET /portal/api/webhooks/{webhook_id}/health.
      operationId: getWebhookHealth
      parameters:
        - description: Tenant consumer the webhook belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Tenant consumer the webhook belongs to.
            type: string
        - description: Webhook id (UUID).
          in: path
          name: webhook_id
          required: true
          schema:
            description: Webhook id (UUID).
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookHealthOutputBody"
          description: OK
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Get health status and metrics for a webhook
      tags:
        - Health
  /v1/consumers/{consumer}/webhooks/{webhook_id}:pause:
    post:
      description: |-
        Stops deliveries to this webhook without deleting it. Returns 204 with no body. Events keep fanning out to it: each new delivery, and any queued or retrying one, is held with status paused until it is retried after resuming. Holding never affects the webhook's health. 409 when the webhook is already paused.

        Portal tokens: allowed, as POST /portal/api/webhooks/{webhook_id}:pause.
      operationId: pauseWebhook
      parameters:
        - description: Tenant consumer the webhook belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Tenant consumer the webhook belongs to.
            type: string
        - description: Webhook id (UUID).
          in: path
          name: webhook_id
          required: true
          schema:
            description: Webhook id (UUID).
            type: string
      responses:
        "204":
          description: No Content
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "409":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Conflict
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Pause a webhook
      tags:
        - Webhooks
  /v1/consumers/{consumer}/webhooks/{webhook_id}:resume:
    post:
      description: |-
        Re-enables deliveries to a webhook paused by an operator or auto-disabled, and clears auto_disabled_at/auto_disabled_reason. Deliveries held while it was paused stay paused and are never sent automatically: the response says how many there are, so you can retry them with status=paused&webhook_id=... and the delivery retry endpoints. 409 when the webhook is already active.

        Portal tokens: allowed, as POST /portal/api/webhooks/{webhook_id}:resume.
      operationId: resumeWebhook
      parameters:
        - description: Tenant consumer the webhook belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Tenant consumer the webhook belongs to.
            type: string
        - description: Webhook id (UUID).
          in: path
          name: webhook_id
          required: true
          schema:
            description: Webhook id (UUID).
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ResumeWebhookOutputBody"
          description: OK
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "409":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Conflict
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Resume a paused webhook
      tags:
        - Webhooks
  /v1/consumers/{consumer}/webhooks/{webhook_id}:rotateSecret:
    post:
      description: |-
        Replaces the webhook's HMAC signing secret with a newly generated one and returns it in plaintext. This response is the only time the new secret is shown; it is masked on every later read. There is no overlap window: every attempt made after the rotation, including retries of older deliveries, is signed with the new secret only, so update the receiver right away. Not idempotent: each call generates a new secret. The Ed25519 key, if any, is not rotated.

        Portal tokens: allowed, as POST /portal/api/webhooks/{webhook_id}:rotateSecret.
      operationId: rotateWebhookSecret
      parameters:
        - description: Tenant consumer the webhook belongs to.
          in: path
          name: consumer
          required: true
          schema:
            description: Tenant consumer the webhook belongs to.
            type: string
        - description: Webhook id (UUID).
          in: path
          name: webhook_id
          required: true
          schema:
            description: Webhook id (UUID).
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RotateWebhookSecretOutputBody"
          description: OK
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Rotate a webhook's signing secret
      tags:
        - Webhooks
  /v1/deliveries:
    get:
      description: |-
        Lists deliveries across every consumer, newest first, with the same filters, cursor pagination and prepare_retry side effect as the per-consumer route. Pass consumer to scope to one consumer.

        Portal tokens: not allowed.
      operationId: listDeliveriesGlobal
      parameters:
        - description: Filter to one consumer; omit to list deliveries across all consumers.
          explode: false
          in: query
          name: consumer
          schema:
            description: Filter to one consumer; omit to list deliveries across all consumers.
            type: string
        - description: Filter to deliveries for one webhook (UUID; 400 otherwise).
          explode: false
          in: query
          name: webhook_id
          schema:
            description: Filter to deliveries for one webhook (UUID; 400 otherwise).
            type: string
        - description: Filter to deliveries for one pushed event occurrence (UUID; 400 otherwise).
          explode: false
          in: query
          name: event_id
          schema:
            description: Filter to deliveries for one pushed event occurrence (UUID; 400 otherwise).
            type: string
        - description: Filter by delivery status.
          explode: false
          in: query
          name: status
          schema:
            description: Filter by delivery status.
            enum:
              - pending
              - sending
              - success
              - failed
              - retrying
              - expired
              - paused
            type: string
        - description: Filter by the most recent attempt's error_category, e.g. server_error, client_error, timeout.
          explode: false
          in: query
          name: error_category
          schema:
            description: Filter by the most recent attempt's error_category, e.g. server_error, client_error, timeout.
            type: string
        - description: Filter to deliveries created by one subscription (UUID), e.g. its paused deliveries.
          explode: false
          in: query
          name: subscription_id
          schema:
            description: Filter to deliveries created by one subscription (UUID), e.g. its paused deliveries.
            type: string
        - description: Filter to deliveries of one event type, e.g. order.created. Also matches deliveries whose subscription has since been replaced or deleted.
          explode: false
          in: query
          name: event_name
          schema:
            description: Filter to deliveries of one event type, e.g. order.created. Also matches deliveries whose subscription has since been replaced or deleted.
            type: string
        - description: "Only deliveries created at or after this time: YYYY-MM-DD (start of that day, UTC) or an RFC3339 timestamp (e.g. an import's imported_at)."
          explode: false
          in: query
          name: created_after
          schema:
            description: "Only deliveries created at or after this time: YYYY-MM-DD (start of that day, UTC) or an RFC3339 timestamp (e.g. an import's imported_at)."
            type: string
        - description: "Only deliveries created at or before this time: YYYY-MM-DD (through the end of that day, UTC) or an RFC3339 timestamp."
          explode: false
          in: query
          name: created_before
          schema:
            description: "Only deliveries created at or before this time: YYYY-MM-DD (through the end of that day, UTC) or an RFC3339 timestamp."
            type: string
        - description: "Side effect: when true, also snapshots the ids of every delivery matching the filters (not just this page; at most 10,000, more is an error) into a batch job and returns its id as retry_id. Pass it to POST .../deliveries:retryBatch within 15 minutes. Each call creates a new snapshot."
          explode: false
          in: query
          name: prepare_retry
          schema:
            default: false
            description: "Side effect: when true, also snapshots the ids of every delivery matching the filters (not just this page; at most 10,000, more is an error) into a batch job and returns its id as retry_id. Pass it to POST .../deliveries:retryBatch within 15 minutes. Each call creates a new snapshot."
            type: boolean
        - description: Maximum items to return, 1 to 1000.
          explode: false
          in: query
          name: limit
          schema:
            default: 50
            description: Maximum items to return, 1 to 1000.
            format: int32
            maximum: 1000
            minimum: 1
            type: integer
        - description: "Continue after the page this came from: pagination.next_cursor of the previous response. Stable while new deliveries arrive."
          explode: false
          in: query
          name: cursor
          schema:
            description: "Continue after the page this came from: pagination.next_cursor of the previous response. Stable while new deliveries arrive."
            type: string
        - description: "Removed: page with cursor. Any value other than 0 is rejected with 400, so a client still paging by offset fails loudly instead of re-reading the first page."
          explode: false
          in: query
          name: offset
          schema:
            default: 0
            description: "Removed: page with cursor. Any value other than 0 is rejected with 400, so a client still paging by offset fails loudly instead of re-reading the first page."
            format: int32
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListDeliveriesOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: List deliveries across all consumers
      tags:
        - Deliveries
  /v1/deliveries/{delivery_id}:
    get:
      description: |-
        Looks a delivery up by id in any consumer, for callers that only have the id (e.g. the X-Sparrow-Delivery-ID header a receiver logged). 404 when it does not exist.

        Portal tokens: not allowed.
      operationId: getDeliveryGlobal
      parameters:
        - description: Delivery id (UUID).
          in: path
          name: delivery_id
          required: true
          schema:
            description: Delivery id (UUID).
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeliveryItem"
          description: OK
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Get a delivery by id (any consumer)
      tags:
        - Deliveries
  /v1/deliveries/{delivery_id}/attempts:
    get:
      description: |-
        Same as getDeliveryAttempts, without a consumer: the delivery is looked up by id alone, and an unknown id returns an empty list.

        Portal tokens: not allowed.
      operationId: getDeliveryAttemptsGlobal
      parameters:
        - description: Delivery id (UUID).
          in: path
          name: delivery_id
          required: true
          schema:
            description: Delivery id (UUID).
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AttemptsOutputBody"
          description: OK
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Get a delivery's per-attempt history (any consumer)
      tags:
        - Deliveries
  /v1/deliveries/{delivery_id}:retry:
    post:
      description: |-
        Same as retryDelivery, for a delivery in any consumer.

        Portal tokens: not allowed.
      operationId: retryDeliveryGlobal
      parameters:
        - description: Delivery id (UUID).
          in: path
          name: delivery_id
          required: true
          schema:
            description: Delivery id (UUID).
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RetryOutputBody"
          description: OK
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "409":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Conflict
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Retry a single delivery (any consumer)
      tags:
        - Deliveries
  /v1/event-types:
    get:
      description: |-
        Lists registered event types sorted by name, optionally only active ones. Sparrow's own sparrow.* events are listed only with consumer=_sparrow. Offset paginated with total_count.

        Portal tokens: allowed, as GET /portal/api/event-types; consumer is set to the token's consumer.
      operationId: listEventTypes
      parameters:
        - description: true returns only active event types; false returns all.
          explode: false
          in: query
          name: active_only
          schema:
            default: false
            description: true returns only active event types; false returns all.
            type: boolean
        - description: "Return the event types this consumer can subscribe to: Sparrow's own sparrow.* events for the _sparrow consumer, tenant event types for any other consumer or when omitted."
          explode: false
          in: query
          name: consumer
          schema:
            description: "Return the event types this consumer can subscribe to: Sparrow's own sparrow.* events for the _sparrow consumer, tenant event types for any other consumer or when omitted."
            type: string
        - description: Maximum items to return, 1 to 1000.
          explode: false
          in: query
          name: limit
          schema:
            default: 50
            description: Maximum items to return, 1 to 1000.
            format: int32
            maximum: 1000
            minimum: 1
            type: integer
        - description: Number of items to skip, for pagination.
          explode: false
          in: query
          name: offset
          schema:
            default: 0
            description: Number of items to skip, for pagination.
            format: int32
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListEventTypesOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: List event type definitions
      tags:
        - Event Types
    post:
      description: |-
        Defines a named event type at version 1, optionally with a JSON Schema; the response includes a sample_payload generated from the schema. Pushing an event requires its type to be registered (unless SPARROW_AUTO_REGISTER_EVENTS=true). Event types are never deleted; deactivate one with active: false.

        Not idempotent: 409 when the name already exists (use PATCH, or :import, to change it). 400 for a missing, too long or reserved (sparrow.*) name or an invalid schema.

        Portal tokens: not allowed.
      operationId: registerEventType
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EventTypeBody"
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EventTypeItem"
          description: Created
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "409":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Conflict
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Register an event type definition
      tags:
        - Event Types
  /v1/event-types/{name}:
    get:
      description: |-
        Returns one event type's current version: schema, sample payload, and metadata. 404 when the name is not registered.

        Portal tokens: allowed, as GET /portal/api/event-types/{name}.
      operationId: getEventType
      parameters:
        - description: Event type name, e.g. order.created.
          in: path
          name: name
          required: true
          schema:
            description: Event type name, e.g. order.created.
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EventTypeItem"
          description: OK
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Get an event type by name
      tags:
        - Event Types
    patch:
      description: |-
        Partially updates an event type: only fields present in the request body are changed (metadata, when sent, replaces the whole map). A schema change creates a new version and keeps the previous one; adding a first schema to a schema-less version, and any change to description, metadata or active, updates the current version in place. The response's change field says which happened; repeating the same body is a no-op (change.action unchanged).

        A breaking schema change (see change.compatibility) to an event type that subscriptions receive is refused with 409 unless allow_breaking=true. 404 when the name is not registered.

        Portal tokens: not allowed.
      operationId: updateEventType
      parameters:
        - description: Event type name, e.g. order.created.
          in: path
          name: name
          required: true
          schema:
            description: Event type name, e.g. order.created.
            type: string
        - description: Apply a breaking schema change even though subscriptions receive this event type. Without it, a breaking change to a subscribed type is refused with 409 and the reasons.
          explode: false
          in: query
          name: allow_breaking
          schema:
            default: false
            description: Apply a breaking schema change even though subscriptions receive this event type. Without it, a breaking change to a subscribed type is refused with 409 and the reasons.
            type: boolean
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EventTypeBody"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UpdateEventTypeOutputBody"
          description: OK
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "409":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Conflict
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Update an event type definition
      tags:
        - Event Types
  /v1/event-types/{name}/versions:
    get:
      description: |-
        Returns the version history of an event type, newest first. Not paginated. Event types are never deleted; to retire one, set active to false.

        Portal tokens: allowed, as GET /portal/api/event-types/{name}/versions.
      operationId: listEventTypeVersions
      parameters:
        - description: Event type name, e.g. order.created.
          in: path
          name: name
          required: true
          schema:
            description: Event type name, e.g. order.created.
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListEventTypeVersionsOutputBody"
          description: OK
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: List every version of an event type
      tags:
        - Event Types
  /v1/event-types/{name}/versions/{version}:
    get:
      description: |-
        Returns one version's schema, sample payload and description. 404 when the name or version does not exist.

        Portal tokens: allowed, as GET /portal/api/event-types/{name}/versions/{version}.
      operationId: getEventTypeVersion
      parameters:
        - description: Event type name, e.g. order.created.
          in: path
          name: name
          required: true
          schema:
            description: Event type name, e.g. order.created.
            type: string
        - description: Version number.
          in: path
          name: version
          required: true
          schema:
            description: Version number.
            format: int64
            minimum: 1
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EventTypeVersionItem"
          description: OK
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Get one version of an event type
      tags:
        - Event Types
  /v1/event-types/{name}:validate:
    post:
      description: |-
        Checks a payload against the event type's current JSON Schema without pushing an event or storing anything. pushEvent accepts events regardless of schema validity; this lets callers (e.g. the Push Test Event UI) enforce a hard gate before submitting. An invalid payload is still 200, with valid: false. 404 when the name is not registered.

        Portal tokens: not allowed.
      operationId: validateEventPayload
      parameters:
        - description: Event type name, e.g. order.created.
          in: path
          name: name
          required: true
          schema:
            description: Event type name, e.g. order.created.
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ValidateEventPayloadInputBody"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ValidateEventPayloadOutputBody"
          description: OK
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Validate a payload against an event type's JSON Schema
      tags:
        - Event Types
  /v1/event-types:export:
    post:
      description: |-
        Returns the current definitions of the selected event types as one JSON bundle, ready to import into another environment with importEventTypes. Choose exactly one of names, prefix or all. An unknown name fails the whole export. Sparrow's own sparrow.* event types are never exported. Read-only despite POST (the selection travels in the body).

        Portal tokens: not allowed.
      operationId: exportEventTypes
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ExportEventTypesInputBody"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EventTypeBundle"
          description: OK
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Export event type definitions as a bundle
      tags:
        - Event Types
  /v1/event-types:import:
    post:
      description: |-
        Applies a bundle in one transaction: each item replaces the named event type using the usual version rules, or nothing is written. Event types not in the bundle are never touched; an import does not delete. The full per-item result is returned either way. Nothing is written for a dry run, when a stamp warning is not acknowledged, or when a change is breaking for subscriptions and allow_breaking is not set; blocked_by says why. The body may be an exported bundle as-is, plus the options. Idempotent: importing the same bundle again reports every item unchanged. The body is limited to 5 MiB and 500 items.

        Portal tokens: not allowed.
      operationId: importEventTypes
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ImportEventTypesInputBody"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ImportEventTypesOutputBody"
          description: OK
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Import a bundle of event type definitions
      tags:
        - Event Types
  /v1/events:
    get:
      description: |-
        Lists event occurrences across every consumer, newest first, with the same filters, cursor pagination and prepare_repush side effect as the per-consumer route. Pass consumer to scope to one consumer.

        Portal tokens: not allowed.
      operationId: listEventOccurrencesGlobal
      parameters:
        - description: Filter to one consumer; omit to list occurrences across all consumers.
          explode: false
          in: query
          name: consumer
          schema:
            description: Filter to one consumer; omit to list occurrences across all consumers.
            type: string
        - description: Filter to occurrences of this event type name.
          explode: false
          in: query
          name: event
          schema:
            description: Filter to occurrences of this event type name.
            type: string
        - description: "Filter by schema validation result: valid or invalid. Other values are 400."
          explode: false
          in: query
          name: schema_valid
          schema:
            description: "Filter by schema validation result: valid or invalid. Other values are 400."
            type: string
        - description: Filter by labels as comma-separated key=value pairs, e.g. region=eu,tier=gold. Matches occurrences whose labels contain every pair.
          explode: false
          in: query
          name: labels
          schema:
            description: Filter by labels as comma-separated key=value pairs, e.g. region=eu,tier=gold. Matches occurrences whose labels contain every pair.
            type: string
        - description: "Only occurrences created at or after this time: YYYY-MM-DD (start of that day, UTC) or an RFC3339 timestamp."
          explode: false
          in: query
          name: created_after
          schema:
            description: "Only occurrences created at or after this time: YYYY-MM-DD (start of that day, UTC) or an RFC3339 timestamp."
            type: string
        - description: "Only occurrences created at or before this time: YYYY-MM-DD (through the end of that day, UTC) or an RFC3339 timestamp."
          explode: false
          in: query
          name: created_before
          schema:
            description: "Only occurrences created at or before this time: YYYY-MM-DD (through the end of that day, UTC) or an RFC3339 timestamp."
            type: string
        - description: "Side effect: when true, also snapshots the ids of every occurrence matching the filters (not just this page; at most 10,000, more is an error) into a batch job and returns its id as repush_id. Pass it to POST .../events:rePush within 15 minutes. Each call creates a new snapshot."
          explode: false
          in: query
          name: prepare_repush
          schema:
            default: false
            description: "Side effect: when true, also snapshots the ids of every occurrence matching the filters (not just this page; at most 10,000, more is an error) into a batch job and returns its id as repush_id. Pass it to POST .../events:rePush within 15 minutes. Each call creates a new snapshot."
            type: boolean
        - description: Maximum items to return, 1 to 1000.
          explode: false
          in: query
          name: limit
          schema:
            default: 50
            description: Maximum items to return, 1 to 1000.
            format: int32
            maximum: 1000
            minimum: 1
            type: integer
        - description: "Continue after the page this came from: pagination.next_cursor of the previous response. Stable while new events arrive."
          explode: false
          in: query
          name: cursor
          schema:
            description: "Continue after the page this came from: pagination.next_cursor of the previous response. Stable while new events arrive."
            type: string
        - description: "Removed: page with cursor. Any value other than 0 is rejected with 400, so a client still paging by offset fails loudly instead of re-reading the first page."
          explode: false
          in: query
          name: offset
          schema:
            default: 0
            description: "Removed: page with cursor. Any value other than 0 is rejected with 400, so a client still paging by offset fails loudly instead of re-reading the first page."
            format: int32
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListEventOccurrencesOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: List pushed event occurrences across all consumers
      tags:
        - Events
  /v1/events/{event_id}:
    get:
      description: |-
        Returns one event occurrence's payload, labels, and delivery outcome counts. Event ids are globally unique, so no consumer is required. 400 when event_id is not a UUID, 404 when it does not exist (or was removed by retention).

        Portal tokens: not allowed.
      operationId: getEventOccurrence
      parameters:
        - description: Event occurrence id (UUID).
          in: path
          name: event_id
          required: true
          schema:
            description: Event occurrence id (UUID).
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EventOccurrenceItem"
          description: OK
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Get a pushed event occurrence by id
      tags:
        - Events
  /v1/events/{event_id}:repush:
    post:
      description: |-
        Pushes a stored event occurrence again: creates a new event occurrence (new event_id) with the same consumer, event type, payload, labels, metadata and TTL, validates the payload against the current schema, and fans it out to the subscriptions that match now. The original occurrence and its deliveries are unchanged. Not idempotent: each call creates another event. To resend one existing delivery instead, retry the delivery.

        404 when the event does not exist; the same errors as pushEvent when the event type is now inactive or unregistered.

        Portal tokens: not allowed.
      operationId: repushEvent
      parameters:
        - description: Event occurrence id (UUID).
          in: path
          name: event_id
          required: true
          schema:
            description: Event occurrence id (UUID).
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RepushEventOutputBody"
          description: OK
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "409":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Conflict
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Replay a single pushed event occurrence
      tags:
        - Events
  /v1/health-summary:
    get:
      description: |-
        Returns how many webhooks are currently healthy, degraded, unhealthy, or unknown, in one consumer or across every consumer — for a top-level dashboard tile, plus the thresholds behind those statuses. Deleted webhooks are not counted.

        Portal tokens: not allowed.
      operationId: getHealthSummary
      parameters:
        - description: Count one consumer's webhooks; omit to count across all consumers.
          explode: false
          in: query
          name: consumer
          schema:
            description: Count one consumer's webhooks; omit to count across all consumers.
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthSummaryOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: Get aggregate webhook health counts
      tags:
        - Health
  /v1/invites:
    get:
      description: |-
        Lists invites, newest first. Only pending invites unless include_inactive is set. Secrets are never returned. Not paginated.

        Portal tokens: not allowed.
      operationId: listInvites
      parameters:
        - description: Also return redeemed, cancelled, and expired invites.
          explode: false
          in: query
          name: include_inactive
          schema:
            description: Also return redeemed, cancelled, and expired invites.
            type: boolean
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListInvitesOutputBody"
          description: OK
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
        "503":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Service Unavailable
      summary: List invites
      tags:
        - Access
    post:
      description: |-
        Creates a single-use, expiring invite. Opening the returned path in the UI exchanges it for a new access token named after the invitee (POST /invite/redeem), so nobody pastes a key into chat. A consumer invite opens that consumer's portal instead of the operator console. The secret is returned only here. Redeeming happens at POST /invite/redeem (outside /v1, no credential needed); an unknown, used, cancelled or expired invite gets 400 invalid_invite there.

        Portal tokens: not allowed.
      operationId: createInvite
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateInviteBody"
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateInviteOutputBody"
          description: Created
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
        "503":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Service Unavailable
      summary: Invite someone with a one-time link
      tags:
        - Access
  /v1/invites/{invite_id}:
    delete:
      description: |-
        Cancels a pending invite so it can no longer be redeemed. Returns 204. 409 when the invite is no longer pending (redeemed, cancelled or expired); a token already created from it keeps working, so revoke that separately. 404 for an unknown id.

        Portal tokens: not allowed.
      operationId: cancelInvite
      parameters:
        - description: Invite id (the id field, not the secret).
          in: path
          name: invite_id
          required: true
          schema:
            description: Invite id (the id field, not the secret).
            type: string
      responses:
        "204":
          description: No Content
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "409":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Conflict
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
        "503":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Service Unavailable
      summary: Cancel a pending invite
      tags:
        - Access
  /v1/recipes:
    get:
      description: |-
        Lists the adapter recipes built into this Sparrow server. A recipe pre-fills a webhook destination, headers and a subscription transform; clients (the UI, sparrow use) substitute the params and register the webhook. Static and read-only.

        Portal tokens: not allowed.
      operationId: listRecipes
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListRecipesOutputBody"
          description: OK
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: List shipped adapter recipes
      tags:
        - Recipes
  /v1/stats:
    get:
      description: |-
        Returns the same counters as the per-consumer stats endpoint, summed across every consumer, with the same lag.

        Portal tokens: not allowed.
      operationId: getGlobalStats
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConsumerStatsOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: Get aggregate delivery statistics across all consumers
      tags:
        - Webhooks
  /v1/subscriptions:
    get:
      description: |-
        Lists subscriptions across every consumer with the same filters and pagination as the per-consumer route. Pass consumer to scope to one consumer.

        Portal tokens: not allowed.
      operationId: listSubscriptionsGlobal
      parameters:
        - description: Filter to one consumer; omit to list subscriptions across all consumers.
          explode: false
          in: query
          name: consumer
          schema:
            description: Filter to one consumer; omit to list subscriptions across all consumers.
            type: string
        - description: "Filter to subscriptions for one webhook (UUID). Takes precedence over event_name: when both are given, event_name is ignored."
          explode: false
          in: query
          name: webhook_id
          schema:
            description: "Filter to subscriptions for one webhook (UUID). Takes precedence over event_name: when both are given, event_name is ignored."
            type: string
        - description: Filter to subscriptions with exactly this event_name ("*" matches only catch-all subscriptions).
          explode: false
          in: query
          name: event_name
          schema:
            description: Filter to subscriptions with exactly this event_name ("*" matches only catch-all subscriptions).
            type: string
        - description: Maximum items to return, 1 to 1000.
          explode: false
          in: query
          name: limit
          schema:
            default: 50
            description: Maximum items to return, 1 to 1000.
            format: int32
            maximum: 1000
            minimum: 1
            type: integer
        - description: Number of items to skip, for pagination.
          explode: false
          in: query
          name: offset
          schema:
            default: 0
            description: Number of items to skip, for pagination.
            format: int32
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListSubscriptionsOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: List subscriptions across all consumers
      tags:
        - Subscriptions
  /v1/subscriptions:draftTemplate:
    post:
      description: |-
        Drafts a transform_template from a plain-language description, grounded in the event type's JSON Schema and sample payload, the template helper catalog, and optionally a shipped recipe's destination format or an example body the receiver expects. Every draft is rendered against the sample payload (as POST /v1/subscriptions:testTemplate does) and repaired until it renders, so the returned template is known to work. Nothing is saved: put the template into a subscription's transform_template. Requires SPARROW_AI_ENABLED=true and the provider settings (SPARROW_AI_API_KEY for Anthropic, or SPARROW_AI_PROVIDER=openai with SPARROW_AI_BASE_URL and SPARROW_AI_MODEL for any OpenAI-compatible server); otherwise 503, naming the missing setting. Only the sample payload (registered or provided in the request), the schema, and the request's own text are sent to the model, never stored events, headers, or secrets.

        Not idempotent (model output varies). 404 for an unknown event type or recipe, 429 when the provider rate-limits, 504 when the draft takes longer than SPARROW_AI_TIMEOUT (default 3m).

        Portal tokens: not allowed.
      operationId: draftSubscriptionTemplate
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DraftTemplateBody"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DraftTemplateOutputBody"
          description: OK
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "429":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Too Many Requests
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
        "503":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Service Unavailable
        "504":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Gateway Timeout
      summary: Draft a transform template with AI
      tags:
        - Subscriptions
  /v1/subscriptions:draftTemplatePrompt:
    post:
      description: |-
        Returns the same prompt POST /v1/subscriptions:draftTemplate would send, as one self-contained text to paste into any chat assistant (ChatGPT, Claude, a local model). Works whether or not the server has an AI provider configured, so installs without SPARROW_AI_* still get a one-click prompt. The user pastes the assistant's template back into the subscription editor and checks it with Run Preview. Nothing is sent anywhere by Sparrow; a docs_url, if given, is fetched from a public address only to include an excerpt. No side effects.

        Portal tokens: not allowed.
      operationId: buildSubscriptionTemplatePrompt
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DraftTemplateBody"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DraftTemplatePromptOutputBody"
          description: OK
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Build the AI drafting prompt without calling a model
      tags:
        - Subscriptions
  /v1/subscriptions:testTemplate:
    post:
      description: |-
        Dry-runs a transform template against payload when given, else the named event type's generated sample payload, without creating a subscription or any delivery. Use this to iterate on transform_template before saving it; pass a real event's payload to see what a receiver would get. Stateless and idempotent.

        400 when the template does not parse or fails to render (with template_missing_key error, a key the payload lacks is an error), 404 when the event type is not registered, 503 when the template engine is at capacity (retry).

        Portal tokens: allowed, as POST /portal/api/subscriptions:testTemplate.
      operationId: testSubscriptionTemplate
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TestTemplateBody"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TestTemplateOutputBody"
          description: OK
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
        "503":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Service Unavailable
      summary: Render a transform template against a sample or given payload
      tags:
        - Subscriptions
  /v1/template-functions:
    get:
      description: |-
        Lists the helper functions available to subscription transform templates (on top of Go text/template built-ins), each with Markdown documentation. Static: the same for every caller.

        Portal tokens: allowed, as GET /portal/api/template-functions.
      operationId: getTemplateFunctions
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TemplateFunctionsOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: List available payload-transformation template functions
      tags:
        - Webhooks
  /v1/tls/crls:
    get:
      description: Shows the CRLs the instance that answers has cached for checking intermediates in receivers' TLS chains, with when each was fetched and any error. CRLs are downloaded the first time a chain needs them, refreshed in the background by the next handshake once older than SPARROW_CRL_MAX_AGE, and on demand with refreshCrls. Each instance keeps its own cache, so behind a load balancer this shows one instance.
      operationId: listCrls
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListCrlsOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: List cached CRLs
      tags:
        - Server
  /v1/tls/crls:refresh:
    post:
      description: "Asks every instance (through Postgres NOTIFY) to download its cached CRLs now, for when a CA revokes an intermediate and deliveries should stop trusting it before SPARROW_CRL_MAX_AGE passes. Returns 202 once the request is sent; check listCrls for the result. One request a minute on each server: more answer 429. 409 when SPARROW_TLS_REVOCATION=off."
      operationId: refreshCrls
      responses:
        "202":
          description: Accepted
        "409":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Conflict
        "429":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Too Many Requests
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
      summary: Refresh cached CRLs on every instance
      tags:
        - Server
  /v1/tokens:
    get:
      description: |-
        Lists access tokens, newest first. Secrets are never returned. Revoked and expired tokens are hidden unless include_inactive is set. Not paginated.

        Portal tokens: not allowed.
      operationId: listTokens
      parameters:
        - description: Only tokens limited to this consumer. Omit to list every token, tenant-wide ones included.
          explode: false
          in: query
          name: consumer
          schema:
            description: Only tokens limited to this consumer. Omit to list every token, tenant-wide ones included.
            type: string
        - description: Also return revoked and expired tokens.
          explode: false
          in: query
          name: include_inactive
          schema:
            description: Also return revoked and expired tokens.
            type: boolean
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListTokensOutputBody"
          description: OK
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
        "503":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Service Unavailable
      summary: List access tokens
      tags:
        - Access
    post:
      description: |-
        Creates a named token. A tenant-wide token (no consumer) works exactly like SPARROW_API_KEY but can be revoked on its own and never needs the master key to be shared. A consumer token only works through the portal API (/portal/api/), limited to that consumer, and comes with a ready-made portal link (portal_path) to embed or hand to the end consumer. The secret is returned once, or again for the same external_id while the token is valid. Without external_id every call creates a new token.

        400 for an invalid consumer, both ttl_seconds and never_expires, never_expires or more than 30 days on a consumer token, or external_id without a consumer. 503 when the token store is unavailable, or for external_id when no keyring is configured.

        Portal tokens: not allowed.
      operationId: createToken
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateTokenBody"
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateTokenOutputBody"
          description: Created
        "400":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Bad Request
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
        "503":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Service Unavailable
      summary: Create an access token
      tags:
        - Access
  /v1/tokens/{token_id}:
    delete:
      description: |-
        Revokes a token: it stops working immediately on this server and within 30 seconds on other instances. Revoking is permanent and idempotent (204 again for an already revoked token). Returns 204; 404 for an unknown id.

        Portal tokens: not allowed.
      operationId: revokeToken
      parameters:
        - description: Token id (the id field, not the secret).
          in: path
          name: token_id
          required: true
          schema:
            description: Token id (the id field, not the secret).
            type: string
      responses:
        "204":
          description: No Content
        "404":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Not Found
        "422":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Unprocessable Entity
        "500":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Internal Server Error
        "503":
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Service Unavailable
      summary: Revoke an access token
      tags:
        - Access
  /v1/webhooks:
    get:
      description: |-
        Lists webhooks across every consumer, newest first, with masked secrets. Pass consumer to scope to one consumer, health to filter by label, or webhook_id for an id-only lookup when the consumer is not known. Offset paginated with total_count.

        Portal tokens: not allowed.
      operationId: listWebhooksByHealth
      parameters:
        - description: Filter to one consumer; omit to list webhooks across all consumers.
          explode: false
          in: query
          name: consumer
          schema:
            description: Filter to one consumer; omit to list webhooks across all consumers.
            type: string
        - description: Filter to webhooks with this computed health status.
          explode: false
          in: query
          name: health
          schema:
            description: Filter to webhooks with this computed health status.
            enum:
              - healthy
              - degraded
              - unhealthy
              - unknown
            type: string
        - description: Look up a single webhook by id when its consumer is unknown.
          explode: false
          in: query
          name: webhook_id
          schema:
            description: Look up a single webhook by id when its consumer is unknown.
            type: string
        - description: Maximum items to return; values above 1000 are capped at 1000.
          explode: false
          in: query
          name: limit
          schema:
            default: 50
            description: Maximum items to return; values above 1000 are capped at 1000.
            format: int32
            type: integer
        - description: Number of items to skip, for pagination.
          explode: false
          in: query
          name: offset
          schema:
            default: 0
            description: Number of items to skip, for pagination.
            format: int32
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListWebhooksOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: List webhooks across all consumers
      tags:
        - Health
  /v1/whoami:
    get:
      description: |-
        Reports whether auth is on and which credential (master key or access token) authenticated this request. No side effects. Useful to check a key or token before using it.

        Portal tokens: not allowed.
      operationId: getWhoAmI
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WhoAmIOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: Show which credential this request used
      tags:
        - Access
security:
  - ApiKeyAuth: []
  - {}
tags:
  - description: Registered HTTP endpoints and their delivery configuration (retries, timeouts, signing, rate limits). Secrets are always masked in responses except once, at creation time.
    name: Webhooks
  - description: Named, versioned schemas for the events your system can produce. Schema validation is soft — invalid payloads are stored with warnings, not rejected.
    name: Event Types
  - description: Pushed occurrences of an event type. Pushing an event asynchronously fans it out to every subscription that matches its event name and label filters.
    name: Events
  - description: The link between one webhook and one event type within a consumer. Carries the optional payload-transform template and label filters that decide whether a given event occurrence produces a delivery.
    name: Subscriptions
  - description: Individual delivery attempts and their per-attempt retry history. Failed deliveries can be retried singly or in bulk via a prepared batch snapshot.
    name: Deliveries
  - description: Rolling per-webhook health status computed from recent delivery outcomes, plus cross-consumer aggregate counts for dashboards.
    name: Health
  - description: Opt-in email recipients for Sparrow's own self-generated webhook.health_changed, webhook.delivery_failed and webhook.disabled system events.
    name: Alert Configs
  - description: Shipped webhook adapter recipes that pre-fill destinations, headers, and payload transforms.
    name: Recipes
  - description: Access tokens and invites. Tenant-wide tokens work like SPARROW_API_KEY but can be revoked one by one; consumer-scoped tokens only reach the portal API. Secrets are shown once.
    name: Access
  - description: Deployment-level information about this server, such as which optional features are configured.
    name: Server