AsyncAPI
AsyncAPI describes an interface where messages arrive rather than being requested: channels, the operations that send or receive on them, and the message envelope. Connect publishes one at /developers/asyncapi.yaml to 3.1.0, covering outbound webhooks — one channel, one envelope, signed.
Why request/response description does not fit events#
An OpenAPI operation is a question and an answer in one exchange. An event has neither shape: nobody asked, the receiver did not choose the moment, and the interesting facts are the envelope, the delivery guarantee and the signature rather than a status code. Describing a webhook as though it were a request the receiver serves gets you an accurate path and none of what a receiver actually needs.
AsyncAPI names the pieces directly. A channel is where messages flow. An operation says whether this application sends or receives on that channel. A message carries headers, a payload schema and examples. Version 3.1.0 makes the send/receive direction explicit rather than leaving a reader to infer it, which matters when the same document describes both sides of a system.
Does Connect use AsyncAPI?#
Yes, as a publisher, and the scope is deliberately one thing. The document describes outbound webhooks: Connect notifying a subscribing system that a conversation was answered, a follow-up fell due, or something needs a person. One channel, workspaceEvents, whose address is the HTTPS endpoint the workspace registered; one operation, receiveEvent, which Connect sends by HTTP POST; one message envelope.
| Part | Contents |
|---|---|
| Body | id, type, occurredAt, workspaceId and a data object |
X-Connect-Signature | sha256= and the hex HMAC-SHA256 of <timestamp>.<raw body> |
X-Connect-Timestamp | Unix seconds at signing — reject anything far from now |
X-Connect-Event | The event type, mirrored from the body so a receiver can route without parsing |
X-Connect-Delivery | Attempt identifier; changes on each retry, unlike id |
Delivery is at-least-once. The id is stable across retries and the delivery identifier is not, which is the pair a receiver needs: de-duplicate on id, log the delivery identifier when something goes wrong. A receiver that treats every POST as a new event will double-count the first time a network hiccup causes a retry.
What is not in this document is as deliberate as what is. Inbound provider callbacks — a carrier posting a call event, a mailbox push, a messaging webhook — are the application's own inbound surface. They are authenticated by provider signature, their shapes change with the provider, and they are not a developer API. Describing them here would invite integration against something nobody promised to keep stable.
Verifying an event before you trust it#
Read the raw body as bytes. Do not parse it yet.
Result You still have exactly what was signed. Re-serialising JSON changes bytes and breaks the comparison.
Recompute the HMAC-SHA256 over
<timestamp>.<raw body>with the subscription secret and compare in constant time.Result A mismatch means the request did not come from Connect, whatever the body claims.
Reject a timestamp more than a few minutes from now.
Result Replay is bounded. A valid signature on an old body is still an old body.
Look up
id. If you have seen it, acknowledge and stop.Result At-least-once delivery becomes effectively-once processing, which is the property your database actually needs.
When AsyncAPI is worth the file#
- More than one message type. With a dozen event types and a shared envelope, a description is cheaper than a page of prose per type.
- Receivers you do not control. Somebody else's team needs the schema, the headers and the retry semantics in a form their tooling reads.
- A schema that is generated. If the envelope is derived from code, the description can be too, and the two cannot drift.
- Not for a single internal callback. One provider webhook with one shape does not need a specification file; it needs a sentence and a signature check.
Connect's document is on the first three counts and pointedly not on the fourth, which is why the internal provider callbacks are excluded.
Questions#
Does AsyncAPI replace OpenAPI?
No; they describe different halves. OpenAPI covers the calls a client makes; AsyncAPI covers the messages that arrive unasked. A system with both an API and webhooks publishes both, and Connect does.
Is the webhook signature the same as the API's authentication?
No. API authentication proves who is calling Connect. A webhook signature proves that a request arriving at *your* endpoint came from Connect. They point in opposite directions and use different secrets — see Verifying a Connect webhook signature.
Should I use webhooks or poll?
Webhooks when you need to react promptly and can host a public HTTPS endpoint that verifies a signature; polling when you cannot. The trade-off is set out in Webhooks or polling.