Connect by JBRH Open Connect

The AsyncAPI description

/developers/asyncapi.yaml is an AsyncAPI 3.1.0 document describing exactly one thing: outbound events Connect delivers by HTTP POST to a URL a workspace has registered. One channel, one message envelope, six event types, an HMAC signature over the raw body. Everything else that moves over a webhook in this product is an inbound provider callback and is not described there.

Status
Available What this means
Audience
developer
Last verified
Product version
6.3.2

What the document contains#

It is generated by tools/docs_machine.py alongside the OpenAPI and Arazzo descriptions, so its info.version tracks the product version rather than being maintained by hand. The whole document is one channel, workspaceEvents, whose address is the subscriber's own URL, and one operation, receiveEvent, whose action is send — Connect is the sender, your endpoint is the receiver.

FieldValue
asyncapi3.1.0
info.titleConnect by JBRH outbound webhooks
defaultContentTypeapplication/json
servers.production.hostconnectbyjbrh.com, protocol https
ChannelsOne: workspaceEvents, address /{subscriberUrl}
OperationsOne: receiveEvent, action: send, HTTP binding POST
MessagesOne: Event, contentType: application/json

One channel is a design decision, not an omission. A receiver that has to handle six event types on one URL writes one signature check and one dispatch table; a receiver with six URLs writes the signature check six times and gets one of them wrong.

The envelope#

The payload schema is Event, and five fields are required: id, type, occurredAt, workspaceId and data. Only data varies by type, and it is declared additionalProperties: true with an instruction attached — a receiver must ignore fields it does not recognise, because new ones can appear inside data without the envelope changing.

{
  "id": "ev_01J8Z6QK9M",
  "type": "followup.due",
  "occurredAt": "2026-09-10T09:15:00Z",
  "workspaceId": "ws_example",
  "data": {
    "followUpId": "fu_01J8Z6QK9M",
    "channel": "phone",
    "reason": "Call back about the quotation",
    "dueAt": "2026-09-10T09:15:00Z"
  }
}

type is a closed enumeration of six values: conversation.received, conversation.replied, followup.due, followup.completed, needsyou.created and call.ended. They are the events a business acts on, not a log of everything the engine does. workspaceId is documented as "always your own" — a subscription is scoped to one workspace and cannot receive another's events.

The headers, and where the signature is specified#

The message declares three required headers and one optional one. The signature is specified on the operation rather than in prose elsewhere, which means a code generator reading the document sees it.

X-Connect-Signature
sha256= followed by the hex HMAC-SHA256 of <timestamp>.<raw body>, keyed with the subscription secret.
X-Connect-Timestamp
Unix seconds at signing. Reject anything far from now — that is what bounds replay.
X-Connect-Event
The event type, mirrored from the body so a router can dispatch without parsing JSON.
X-Connect-Delivery
Optional. The delivery attempt identifier; it changes on each retry, so it is the wrong thing to de-duplicate on.

The exact computation, and three worked verifiers, are on verifying a Connect webhook signature.

What is deliberately not in it#

The document's own header comment draws the line: internal provider callbacks — a carrier posting a call event, a mail push notification, a WhatsApp webhook — are not described, and are not a developer API. They are the application's inbound surface. They authenticate by provider signature, their shapes are the provider's rather than Connect's, and they change when a provider changes them.

Reading them out of a network trace and building against them would produce a client that breaks on a provider migration nobody announced to you, because from Connect's side that migration is an implementation detail. If you need call or message events, the six types above are the supported surface.

Questions#

Why is there only one channel?

Because there is one delivery mechanism: an HTTP POST to a URL a workspace registers. AsyncAPI models the channel as the address, and the address is yours, so /{subscriberUrl} with one parameter is an honest description. Six channels would imply six endpoints you can subscribe to separately, which is not what the system does.

Can I generate a client from this document?

You can generate the receiving side — models for the envelope, a handler stub per event type, and the header expectations. There is nothing to generate on the sending side, because you never send an event to Connect; the operation's action is send from Connect's point of view.

Does the document describe how a subscription is created?

No. It describes the wire contract for a delivery, not the administration of the subscription. The registration flow is not part of the published public API, and this corpus does not document a create-subscription endpoint because there is no public write endpoint at all.