api/openapi.yaml
Rama main · réplica de lectura del repositorio original.
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