Webhooks

By Orqestra · Published September 25, 2026

Webhooks push Orqestra events to your own system the moment they happen: a customer sends a message, a chat is assigned, a ticket is resolved, a broadcast finishes. Instead of polling the API, you give Orqestra an HTTPS URL and pick the events you care about. Orqestra then sends a signed POST for each one.

Typical uses: copy every conversation into your CRM, open a case in your helpdesk when a chat is escalated, notify a Slack channel when a broadcast completes, or record ad leads in your own analytics.

How it works

  1. Something changes in Orqestra — for example, a customer sends a WhatsApp message.
  2. Orqestra records an event in the same database transaction as the change. An event is never lost, and it never describes a change that did not commit.
  3. Every active endpoint subscribed to that event gets a delivery: an HTTPS POST with a JSON body, signed with that endpoint's secret.
  4. Your server checks the signature and answers 2xx within 10 seconds. If it does not, Orqestra retries for up to 24 hours.

Webhooks and the REST API work together. A webhook tells you that something changed; the API reads the current state and acts on it.

Quick start

1. Build a receiver

You need a public HTTPS URL that accepts POST and answers with any 2xx status. Start with the smallest handler that works, then add signature verification before you act on anything it receives.

import express from 'express'

const app = express()

app.post('/webhooks/orqestra', express.raw({ type: 'application/json' }), (req, res) => {
  const event = JSON.parse(req.body.toString('utf8'))
  console.log(event.type, event.id)
  res.sendStatus(200)
})

app.listen(3000)

Developing locally? Orqestra only delivers to public HTTPS addresses. It refuses localhost and private networks, so expose your local server through an HTTPS tunnel such as ngrok or Cloudflare Tunnel and use the tunnel's URL.

2. Add the endpoint

In the dashboard, open Developers → Webhooks and click Add endpoint.

FieldWhat to enter
NameA label for your team, such as “CRM sync”.
HTTPS endpoint URLYour receiver's full URL. It must be https://, with no username/password and no #fragment. Query strings are allowed.
Channel eventsEvents that happen on a channel — messages, chats, sessions, broadcasts, orders, automation runs — and the channel instances to send them for. Tick each WhatsApp number, Instagram account, etc. that the endpoint should hear about.
Organization eventsEvents that belong to the whole organization: contacts and tickets. Only users with access to every channel can select these.

Nothing is pre-selected, and “select all” selects what exists today. A channel you connect later, or an event type Orqestra adds later, is not sent to an existing endpoint until you edit the endpoint and tick it. That way a new WhatsApp number never starts flowing into an integration that wasn't built for it.

3. Copy the signing secret

Creating the endpoint shows its signing secret once. Store it where your receiver can read it, such as an environment variable or secret manager. If you lose it, use Rotate signing secret to issue a new one. See Rotating the secret.

4. Send a test, then activate

A new endpoint starts as Needs test. Click Send test: Orqestra POSTs a webhook.test event, signed like any real delivery. If your URL answers 2xx, the endpoint becomes Inactive (verified but not yet receiving), and you can click Activate.

Delivery starts at that moment. Events from before activation are never sent: there is no backfill. To catch up on history, read it through the API.

Endpoint status

  • Needs test — new, or its URL changed. Nothing is delivered. Next: send a test that gets a 2xx.
  • Inactive — verified, but switched off. Nothing is delivered, and events that happen now are not queued for later. Next: activate.
  • Active — receiving every subscribed event.

Orqestra never deactivates an endpoint on its own, even if it keeps failing. Failures show up in the endpoint's delivery history instead. See Delivery & Retries.

What a delivery looks like

Every delivery is a POST with these headers:

HeaderValue
Content-Typeapplication/json
User-AgentOrqestra-Webhooks/1.0
X-Orqestra-Event-IdThe event ID — identical to id in the body, and the same on every retry and redelivery. Deduplicate on this.
X-Orqestra-Delivery-IdThis delivery to this endpoint. It stays the same across automatic retries and changes when you redeliver manually.
X-Orqestra-TimestampUnix time in seconds when this attempt was signed.
X-Orqestra-Signaturev1=<hex HMAC-SHA256>. Two comma-separated values during a secret rotation. See Verifying Signatures.

The body is a JSON envelope. Every event has the same outer fields, and what happened is in data:

{
  "apiVersion": "2026-09-01",
  "channel": {
    "id": "cmf0q9b2d0003chn7docs0002",
    "label": "Customer Care WA",
    "type": "WHATSAPP"
  },
  "createdAt": "2026-09-24T02:15:07.000Z",
  "data": {
    "message": {
      "caption": null,
      "chatId": "cmf2c5h8p0013cht7docs0004",
      "contactId": "cmf2c4t7n0011cnt7docs0003",
      "createdAt": "2026-09-24T02:15:07.000Z",
      "deletedAt": null,
      "direction": "INBOUND",
      "externalId": "wamid.HBgNNjI4MTIzNDU2Nzg5MBUCABIYFDNBQjE2QzY0MUE0NzJGOTg4QkQ5AA==",
      "id": "cmf2c7m3s0017msg7docs0009",
      "media": null,
      "referral": null,
      "replyToMessageId": null,
      "sessionId": "cmf2c6k1r0015ses7docs0005",
      "status": "RECEIVED",
      "subject": null,
      "text": "Halo, pesanan saya #1042 sudah dikirim belum?",
      "type": "text",
      "updatedAt": "2026-09-24T02:15:07.000Z"
    }
  },
  "id": "evt_7f3a9c2e41b84d0f9e6a5b3c2d1e0f98",
  "organizationId": "cmf0q8z1k0000org7docs0001",
  "type": "message.received"
}

Envelope fields

FieldDescription
idUnique event ID. Treat it as an opaque string; don't parse the prefix.
typeWhat happened, e.g. message.received. See the Event Reference.
apiVersionThe payload contract version, currently 2026-09-01.
createdAtWhen the change happened in Orqestra (UTC, ISO 8601). Use this for ordering; not the timestamp header, which changes on every retry.
organizationIdYour organization.
channelFor channel events, { id, type, label } of the channel instance, as it was when the event happened. type is one of WHATSAPP, INSTAGRAM, WEBCHAT, TELEGRAM, TIKTOK, EMAIL. For organization events, null.
dataThe event's payload. Its shape depends on type.

Keys arrive in alphabetical order, as compact JSON. Don't depend on key order or whitespace, and ignore fields you don't recognise. New fields can appear within the same apiVersion. Removing, renaming or retyping a field only happens under a new version.

Channel events and organization events

19 event types belong to a channel. They are sent only for the channel instances you tick on the endpoint, and channel in the envelope says which one. 8 event types — contacts and tickets — belong to the organization. They aren't tied to any channel, arrive with channel: null, and ignore the channel selection.

One endpoint can receive both kinds. If it subscribes to any channel event, it needs at least one channel selected before it can be activated.

Managing an endpoint

Each row on Developers → Webhooks has these actions:

  • Edit — change the name, events or channels at any time. Changing the URL sets the endpoint back to Needs test, cancels its pending deliveries, and requires a new successful test before it can be activated again.
  • Send test — sends a webhook.test event. A test never switches off an active endpoint, even when it fails.
  • Rotate signing secret — issues a new secret. The old one keeps working for 24 hours.
  • Delivery history — every delivery from the last 14 days, each attempt's response code and timing, and Redeliver.
  • Deactivate — stops delivery immediately and cancels anything still retrying.
  • Delete — removes the endpoint for good.

Who can manage webhooks

Webhooks share the API tokens permission family. A role needs:

ToPermission
See endpoints and delivery historyapi_tokens.view
Create and edit endpoints, send tests, activate, rotate secrets, redeliverapi_tokens.create
Deactivate or delete endpointsapi_tokens.revoke

A user limited to some channels can only select those channels, can't select organization events, and only sees endpoints that stay within their channels.

Before you go live

  • Verify X-Orqestra-Signature on every request, against the raw body.
  • Store the event (or put it on a queue), answer 2xx, then do the slow work.
  • Deduplicate on the event id. The same event can arrive more than once.
  • Don't assume events arrive in order. Compare createdAt / updatedAt, or read the current state from the API.
  • Ignore unknown fields and unknown event types.
  • Keep the signing secret out of source control, and plan how you will rotate it.