webhook sink, because that is the shortest path: four calls to the hub, and one HTTP request arriving at your endpoint.
Every /v1 call sends Authorization: Bearer <token> and application/json. The hub reads your tenant from the token. It never reads a tenant from a body, a path, or a query.
Before you start
You need four things.
- A running hub and a token. The
/v1control plane authenticates every route with a plugin-auth JWT. The catalog read below also needscatalogget. See Operating Streaming Hub for the deployment. STREAMING_HUB_MANIFEST_SOURCESset on the hub, if you want the catalog read to list your producers. It is empty by default, and an empty list leaves the catalog with the hub’s ownhub.*entries only. See Operating Streaming Hub.- A public
https://endpoint you control. The hub validates the destination before it writes any row, and rejects private, loopback, and cloud-metadata addresses. Alocalhostendpoint cannot be stored. - A producer on the same stream, with publication turned on. Publication is off by default on the producer side. On Midaz, set
STREAMING_ENABLED=true, pointSTREAMING_BROKERSat the same brokers asSTREAMING_HUB_KAFKA_BROKERS, and setSTREAMING_CLOUDEVENTS_SOURCE. See Streaming and outbox. Thece-tenantidthe producer emits must also equal the hub’sSTREAMING_HUB_TENANT_ID. A mismatch drops every event with no error. Operating Streaming Hub states that rule.
The five calls
1. Get the matching key
GET /v1/catalog
The catalog lists what the producer manifests in STREAMING_HUB_MANIFEST_SOURCES declare. Each entry gives an eventType and a topic.
The catalog does not carry the matching key. eventType is the event segment alone. The topic folds the producer’s service name into its resource segment. So lerian.streaming.ledger_organization.created has eventType created, and the key you need is organization.created. Take the key from the per-product pages under Event streaming. The producer’s own /streaming/manifest also reports resourceType next to eventType.
The hub accepts any well-formed key in event_types. A key that no producer emits matches nothing, and the subscription receives no events.
2. Create the subscription
POST /v1/subscriptions
Send the header X-Idempotency with a unique value. The hub rejects a create that omits it, before any write.
event_types holds matching keys, not full CloudEvents types. A matching key is the <resource>.<event> tail — organization.created, and never the full studio.lerian.organization.created. Send the key you assembled in step 1. Omit event_types to receive every event the hub sees.
The per-product pages under Event streaming describe each type in full — start from the Midaz event catalog.
plan_tier defaults to standard. schema_major is optional — leave it out to follow the base version.
The 201 response carries the subscription id and the plaintext signingSecret. Save the secret now. No read path returns it, and if you lose it your only route is rotation.
3. Activate the destination
POST /v1/subscriptions/{id}/ping
A new webhook subscription is born pending_verification. The hub delivers to it only after a probe proves the destination answers, so this call is required. It sends one synthetic signed request through the production delivery path, then reports the result:
outcome: "ok" moves the subscription to active, which is the state that makes it deliverable. Your endpoint must answer with a 2xx for that to happen, so deploy it before you ping.
A probe that ran and failed is still a 200 — read outcome, not the HTTP status. outcome: "failed" leaves the subscription unverified and names the cause in errorClass. Fix the endpoint and ping again. The call is safe to repeat and needs no idempotency key.
Delivery requires enabled and verification_state = active. Managing subscriptions explains why the two fields stay separate.
4. Trigger an event
Do something in a Lerian product that emits the type you subscribed to. In Midaz, creating one organization emits the event above.
POST /v1/organizations
5. Confirm the delivery
Your endpoint receives a
POST carrying the event payload, an HMAC signature, and the hub’s context headers — X-Lerian-Event-Id, X-Lerian-Event-Type, and X-Lerian-Delivery-Id among them. Verify the signature before you trust the body: an unverified request proves nothing. Consuming events has the verification steps, the full header list, and the deduplication rule.
Then ask the hub what it recorded.
GET /v1/subscriptions/{id}/health
One success in
delivery_outcomes and a fresh last_success_at mean the path works end to end. If nothing arrived, status and delivery_outcomes tell you whether the hub tried and failed, or never matched the event at all.
Prefer to pull?
A
pull subscription needs no endpoint and no probe. Create it with sink_kind: "pull" and no endpoint — the hub synthesizes one, and the subscription is active from birth. Read pages of events with GET /v1/events?subscription_id=<id>. The read is the acknowledgment, so read the cursor rules in Consuming events before your first call.
Next steps
Managing subscriptions
Queue sinks, AWS delegated grants, secret rotation, and recovery.
Consuming events
Verify signatures, deduplicate deliveries, and pull with a cursor.
How Streaming Hub works
Matching, dispatch, the retry curve, and auto-disable.
Operating Streaming Hub
Deploy the hub, configure it, and watch it run.

