Skip to main content
Webhooks push events to your server when something happens on the line — for example a message is received or sent. Use them when you want real-time updates instead of polling GET /messages.
Building with the Messages API only? In the dashboard open Lines and choose API / webhooks (or claim a dedicated number with “Use API / webhooks”). That attaches the line to your account without a live no-code agent, so you do not get dual replies from both the agent and your webhook handler. You can also pause any existing agent on the line.

Prerequisites

  • A Messages API key with the comms_webhooks scope.
  • A public HTTPS URL that accepts POST.
  • Prefer API / webhooks inbound mode on Lines so no-code agents do not auto-reply alongside your backend.

Register an endpoint

Scope: comms_webhooks
Success: 201 with the created webhook object, including its signing secret:
webhook.secret is generated server-side on create — you never supply one. Store it wherever your handler runs; you’ll use it to verify every delivery. If you lose it, list webhooks returns it again for your endpoints. The Developers → Webhooks UI also reveals the secret immediately after create. Omitting events subscribes the endpoint to every comms event.

Which workspace gets the event?

Outbound webhooks are workspace-scoped: every matching event for lines owned by your org is delivered to your endpoints. There is no per-line webhook filter on the endpoint object. On a shared platform line, inbound traffic is routed to a workspace by claim keyword / sticky assignment for that contact. The org that owns the resolved route receives comms.message.* (and call/consent) events. Dedicated lines always belong to a single workspace. Every message event includes the line the contact reached (or that you sent from), when known: Use line_e164 as the tenant discriminator when one workspace owns more than one dedicated number. You do not need a dummy no-code agent per customer.

Message edit events

When a contact edits an iMessage, the messaging bridge emits message.edited (header X-Comms-Event: message.edited). Comms updates the stored message and fans out: Payload shape matches other message events, with extra fields on data.message:
Edits do not re-run the agent. Subscribe explicitly to comms.message.edited (or omit events to receive all).

Message reaction events

When a contact adds or removes a native iMessage tapback, the messaging bridge emits message.reaction (header X-Comms-Event: message.reaction). Comms stores the reaction on the target message and fans out:
Reactions do not re-run the agent. SMS cannot carry native tapbacks; outbound react calls return 409 unsupported_transport.

List endpoints

Verify signatures

Every delivery is a POST with two headers: To verify: compute HMAC_SHA256(secret, rawBody), hex-encode it, and constant-time compare against the header value after stripping the sha256= prefix. Read the raw bytes before any JSON parsing — re-serialized JSON will not match.
The payload envelope is the same for every event:
Deliveries retry with exponential backoff (up to 6 attempts over ~1.5 days) and are marked dead after the final failure. The signature covers only the body — there is no signed timestamp — so treat delivery as at-least-once and dedupe on the event’s record id rather than relying on replay protection.

Send a test event

Fire a signed comms.ping at your endpoint without generating real traffic:
The ping goes through the same delivery pipeline as real events — same envelope, same signature — so a 2xx from your handler proves the whole path works. The test request waits for that attempt and returns its delivery status. The endpoint must include comms.ping in its events; otherwise the test returns an error immediately and does not enqueue a delivery.

Local development

  1. Run your app with a POST handler that logs the body.
  2. Tunnel with ngrok / Cloudflare Tunnel.
  3. Register the tunnel URL as above.
  4. Send a test message to your line, or fire a comms.ping with the test endpoint.

Handler checklist

Reject any request whose X-Osis-Signature doesn’t verify against your whsec_ secret. See Verify signatures.
Return 2xx as soon as you accept the event. Do heavy work in a queue.
The same event may arrive more than once. Make handlers idempotent on event id when present.
Webhook receivers belong on your server. Do not process hooks in a browser.