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.
  };
}
FieldMeaning
idUnique event identifier — equivalent to meta.eventId. Stable across retries, so use it as your dedupe key.
typeEvent name literal (see the event catalogue).
timestampWhen the event occurred, ISO-8601 UTC.
dataPer-event payload — the shape is documented on each event's page.
meta.apiVersionPinned to "2026-04-01". Any breaking change to a data shape will bump this; additive changes (new optional field) will not.
meta.attempt1-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>
HeaderPurpose
X-Wexio-EventEvent name (same as type in the body)
X-Wexio-Event-Idevt_<uuid> — use to dedupe retries on your side
X-Wexio-Attempt1-indexed attempt number
X-Wexio-TimestampISO-8601 UTC — same as timestamp
Idempotency-KeySame as X-Wexio-Event-Id — use whichever convention fits your framework
AuthorizationPresent on BEARER / BASIC modes
X-Wexio-SignaturePresent on HMAC mode — t=<unix>,v1=<hex>

Auth modes

Configured per-connection via outboundAuthType + encrypted outboundAuthCredential.

outboundAuthTypeHow the receiver verifies
NONENo auth header. Receiver relies on HTTPS + URL unguessability.
BEARERAuthorization: Bearer <credential> — compare to the shared token.
BASICAuthorization: Basic <base64(credential)> — credential is user:pass.
HMACX-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_ATTEMPTS attempts (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 eventId twice with different attempt values. Dedupe by id.
  • 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.occurredAt before 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 data shape, 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.

On this page