/v1 control-plane API, authenticated with an Access Manager token. This page covers the model and the onboarding flows. The API reference has the exact request and response shapes (start from the reference introduction).
The subscription model
A subscription records:
name: a human label (required, non-blank). A subscription that is not deleted and already holds the name makes the create return409 subscription_name_taken.sink_kind: one ofwebhook,pull,sqs,rabbitmq,eventbridge.endpoint: where deliveries go, in a shape that depends on the sink kind (see below).event_types: the keys to deliver. Take each key from theeventKeyfield ofGET /v1/catalog, exactly as served. The hub accepts any well-formed key, so a catalog gap never stalls onboarding. A key that no producer emits matches nothing. Omit the field to receive every event type.origin: an optional exactce-sourcepin. Omit it to accept the same key from any producing application.schema_major: the payload major version the subscription follows. Omit it to follow major version 1.timeout_ms: for a webhook only, the per-attempt delivery timeout, from 1000 to 20000 milliseconds. The default is 10000.plan_tier: the delivery tier the subscription runs under.
Two orthogonal status fields
Every subscription carries two independent status fields. Confusing them is the most common source of “why did delivery stop” questions, so keep them separate:
verification_stateis proof that the destination can actually receive events. It moves throughpending_verification→active→degraded, driven by probes. Only anactivesubscription is deliverable.enabledis the deliverability switch. It is what auto-disable flips off, and what a re-enable turns back on.
enabled and verification_state = active. Auto-disable lives entirely in enabled and never changes verification_state, so an auto-disabled subscription reads as enabled = false, verification_state = active. See auto-disabling a broken destination for how the two interact.
Creating a webhook subscription
A webhook subscription is the simplest to onboard. It needs no credential, only one probe:
- Create:
POST /v1/subscriptionswithsink_kind: "webhook"and yourhttps://endpoint. Send a uniqueX-Idempotencyheader: the hub rejects a create that omits it, before any write. The hub validates the destination URL against private, loopback, and cloud-metadata address ranges before any write. The response returns the new subscription inpending_verification, and the signing secret. - Save the signing secret: no read route returns it. A retry with the same
X-Idempotencykey returns the same response. The hub stores the secret only as ciphertext. If you lose it, you can only rotate to a new one. - Activate the destination:
POST /v1/subscriptions/:id/pingsends a synthetic, signed probe through the real delivery path and reports the classified outcome. A successful probe moves the subscription toactive, and that is what makes it deliverable. Deploy your endpoint before you ping: it must answer2xx.
Onboarding a queue subscription
Queue subscriptions (
sqs, rabbitmq, eventbridge) are born pending_verification and deliver nothing until a probe verifies their destination. How you verify depends on the kind:
- RabbitMQ: supply a broker credential, verified on write (below).
- SQS and EventBridge: either supply an outbound credential, verified on write (below), or wire an AWS delegated grant (next section) without storing a credential.
- Create:
POST /v1/subscriptionswith the queuesink_kindand its endpoint, and no inline credential (the hub rejects an inlinesink_configorcredential). Send a uniqueX-Idempotencyheader, as for any create. The hub stores the subscriptionpending_verification. Matching excludes it, so it produces no delivery jobs yet. - Supply the credential:
PUT /v1/subscriptions/:id/credentialwith the write-only outbound credential. The hub holds it in memory, probes it immediately (connect and authenticate against the destination), and persists it as ciphertext only if the probe succeeds. A failed probe stores nothing. The hub validates destination addresses against blocked ranges before storage. A successful probe moves the subscription toactive. - Active: once
active, matching admits the subscription and it begins receiving events.
PUT a new one. The same probe-on-write applies.
Wiring an AWS delegated grant
For the credentialless AWS path (
sqs, eventbridge), the hub delivers by assuming a role in your AWS account. You wire that trust instead of supplying an outbound credential:
- Fetch the setup artifacts:
GET /v1/subscriptions/:id/setup-artifactsreturns a cross-account IAM trust policy, a CloudFormation quick-create link, and a non-secretExternalIdthe hub mints for this subscription. - Apply them in your AWS account: create the delivery role from the trust policy (the quick-create link scaffolds it). The role trusts the hub’s principal only under the minted
ExternalIdcondition. That condition closes the confused-deputy gap. A trust policy that omits it fails verification, and never counts as verified. - Register the grant:
PUT /v1/subscriptions/:id/delegated-grantwith the delivery role ARN, region, and destination. This records non-secret coordinates only. It runs no probe and does not changeverification_state. - Verify:
POST /v1/subscriptions/:id/verifyruns the hardened role-assumption probe and, on success, flips the subscription toactive.
ExternalId.
Rotating a signing secret
POST /v1/subscriptions/:id/secret/rotate mints a new webhook signing secret, returns it, and starts a 24-hour dual-sign overlap. During the overlap the hub signs each delivery with both the new and the previous secret and sends two signature headers. Your consumer can therefore switch to the new secret at any point in the window without dropping deliveries.
Rotate cleanly like this:
- Call rotate and save the new secret from the response (alongside the
overlapUntiltimestamp). - Deploy the new secret to your consumer within the overlap window. While your verifier holds both secrets, it accepts a delivery if either signature validates.
- After the overlap, retire the old secret.
Recovering an auto-disabled subscription
When a destination stays broken long enough, the hub auto-disables the subscription by flipping
enabled = false. To recover it:
- Fix the destination (the endpoint, the queue, or the grant).
- Call
POST /v1/subscriptions/:id/verify. It re-probes the destination. On a successful probe, it re-enables the subscription and clears the auto-disable mark, in place and with the same id and signing secret.
active verification state to degraded. Call GET /v1/subscriptions/:id/health for the delivery-health rollup (recent outcomes, dead-letter counts, and the auto-disable verdict). Use it to confirm the destination is healthy before and after.
Changing the schema pin and the timeout
PATCH /v1/subscriptions/:id changes two fields. Every body must carry schema_major:
{"schema_major": 2}pins the subscription to that major version.{"schema_major": null}clears the pin, so the subscription follows major version 1.- On a webhook, add
"timeout_ms"to change the per-attempt timeout, or send0to return to the 10000 default. Send the currentschema_majorwith it to keep the pin.
schema_major, or a schema_major below 1. It never silently ignores them, so you can never believe a forbidden change took effect. The event_types and origin fields are immutable. Create a new subscription when either filter must change.
A PATCH never changes the endpoint, the sink kind, or the secret. It does not reset verification_state and echoes no secret.
Idempotency
The mutating routes (create, delete,
PATCH, secret rotate, and delegated-grant registration) require an idempotency key:
400 missing_idempotency_key. The store is fail-closed: if the idempotency store is unreachable, the request fails rather than risking a double write. That guarantees a replayed create or rotate returns the original secret instead of minting a new one.
- Replaying a committed key within its retention window (24 hours by default) returns the original response byte-for-byte, with
X-Idempotency-Replayed: true. - Reusing a key while its first request still runs, or with a different request body, returns
409 idempotency_conflict. Mint a new key for a corrected request.
ping, verify, PUT /credential, GET /setup-artifacts) are naturally idempotent and require no key. See the platform-wide guidance on retries and idempotency.
Next steps
Consuming events
Verify webhook signatures, deduplicate deliveries, and pull events.
How Streaming Hub works
Matching, dispatch, retries, and auto-disable in detail.

