Webhooks & APIOutbound
Envelope & Delivery
The JSON envelope, HTTP headers, signing scheme, and retry policy for outbound webhooks
Every outbound event — regardless of type — is delivered as a single POST with the same envelope shape, the same headers, and the same retry semantics. This page is the one-stop reference.
Envelope
interface OutboundWebhookEnvelope<TData> {
id: string; // "evt_<uuid>" — same as meta.eventId; unique across retries
type: OutboundWebhookEventName; // e.g. "chat.created", "message.inbound.created", "flow.<suffix>"
timestamp: string; // ISO-8601 UTC — same as meta.occurredAt
data: TData; // per-event payload — see the individual event pages
meta: {
eventId: string;
event: OutboundWebhookEventName;
occurredAt: string; // ISO-8601 UTC
organisationId: string;
connectionId: string;
apiVersion: "2026-04-01";
attempt: number; // 1-indexed retry number. Same (eventId, attempt) is never re-posted.
};
}| Field | Meaning |
|---|---|
id | Unique event identifier — equivalent to meta.eventId. Stable across retries, so use it as your dedupe key. |
type | Event name literal (see the event catalogue). |
timestamp | When the event occurred, ISO-8601 UTC. |
data | Per-event payload — the shape is documented on each event's page. |
meta.apiVersion | Pinned to "2026-04-01". Any breaking change to a data shape will bump this; additive changes (new optional field) will not. |
meta.attempt | 1-indexed retry counter. Same (eventId, attempt) pair is never re-posted. |
HTTP request
POST <outboundUrl>
Content-Type: application/json
X-Wexio-Event: <event name>
X-Wexio-Event-Id: evt_<uuid>
X-Wexio-Attempt: 1
X-Wexio-Timestamp: 2026-04-24T12:00:00.000Z
Idempotency-Key: evt_<uuid>
[Authorization: ...] # BEARER / BASIC mode only
[X-Wexio-Signature: t=…,v1=…] # HMAC mode only
<envelope JSON body>| Header | Purpose |
|---|---|
X-Wexio-Event | Event name (same as type in the body) |
X-Wexio-Event-Id | evt_<uuid> — use to dedupe retries on your side |
X-Wexio-Attempt | 1-indexed attempt number |
X-Wexio-Timestamp | ISO-8601 UTC — same as timestamp |
Idempotency-Key | Same as X-Wexio-Event-Id — use whichever convention fits your framework |
Authorization | Present on BEARER / BASIC modes |
X-Wexio-Signature | Present on HMAC mode — t=<unix>,v1=<hex> |
Auth modes
Configured per-connection via outboundAuthType + encrypted outboundAuthCredential.
outboundAuthType | How the receiver verifies |
|---|---|
NONE | No auth header. Receiver relies on HTTPS + URL unguessability. |
BEARER | Authorization: Bearer <credential> — compare to the shared token. |
BASIC | Authorization: Basic <base64(credential)> — credential is user:pass. |
HMAC | X-Wexio-Signature: t=<unix>,v1=<hex> over ${t}.${rawBody}, SHA-256. |
HMAC verification
const [tPart, v1Part] = header.split(',');
const t = tPart.slice(2); // "t=1714..."
const v1 = v1Part.slice(3); // "v1=<hex>"
const expected = createHmac('sha256', sharedSecret)
.update(`${t}.${rawBody}`)
.digest('hex');
if (!timingSafeEqual(expected, v1)) throw new Error('bad signature');
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) throw new Error('stale');The t value is a unix timestamp; reject anything outside a ±5 minute tolerance to block replays.
Retry policy
- Up to
WEBHOOK_OUTBOUND_MAX_ATTEMPTSattempts (currently 5). - Exponential backoff starting at 2 seconds.
- Retries fire on 5xx responses or network / timeout failures.
- 4xx responses are terminal — no retry (fix the config, then manually retry from the UI).
Each attempt increments meta.attempt and writes a new row to WebhookDeliveryLog. The (eventId, attempt) pair is unique — the same combination is never re-posted.
Processing guarantees
- At-least-once. Retries after 5xx/timeout mean a consumer can see the same
eventIdtwice with differentattemptvalues. Dedupe byid. - Best-effort ordering. Events are enqueued in order but processed in parallel by the delivery worker. Consumers that need strict ordering per chat should sort by
meta.occurredAtbefore applying. - Fire-and-forget. Wexio's domain services never wait on outbound delivery — if your endpoint is down, events queue up for retry, then dead-letter after the max attempts. Downtime on your side does not affect Wexio operation.
- Audit trail. Every attempt (success or failure) is persisted in
WebhookDeliveryLog, queryable via Webhook History. Retention 30 days.
API version
meta.apiVersion is currently "2026-04-01". Treat it as a breaking-change marker:
- Additive changes (new optional field on an existing
datashape, new event type) — version stays the same. - Breaking changes (removed or renamed field, changed field type) — version is bumped, and old connections continue receiving the old version until migrated.
Related
- Outbound Webhooks — overview, auth config, delivery log
- Shared Types —
ChatPayload,ContactPayload,MessagePayload, enums - Webhook History — browsing delivery attempts and the live subscription