Skip to main content

Overview

When you send a message with a callback_url, Oliver POSTs to that URL each time the message’s status changes. The current status is also always available from GET /messages/{id}.

When the webhook fires

Each status is sent once, in order. If statuses change faster than they can be delivered, you get the latest one, e.g. message.read without a separate message.delivered. Branch on status, and treat a status at or past one you already handled as a no-op.

Payload

The body is the message object plus these fields:
string
required
message.{status}, e.g. message.delivered.
string
required
Unique per delivery. Use it to de-duplicate.
string
The reference_id you supplied when sending, if any.
string
rcs or sms: how the client got the message. Can be null on message.sent with channel: "auto", before the carrier reports it.
string
required
The practice the message was sent for.
string
required
When the status changed (ISO 8601).

Verifying Webhook Signatures

Signatures work the same way as the AI outbound outcome webhook and use the same practice secret. When it’s configured, each request carries X-Webhook-Signature: the HMAC-SHA256 hex digest of the raw request body. Verify it with a constant-time comparison before processing the payload.

Delivery and Retries

  • Webhooks are an HTTP POST with a Content-Type: application/json body and a User-Agent of Oliver-Messages/1.0.
  • Your callback_url must be https://. Oliver verifies the TLS certificate and allows 10 seconds for a response.
  • Respond with a 2xx status code to acknowledge receipt.
  • If your endpoint doesn’t respond successfully, Oliver makes up to 3 attempts in total (waiting 2s, then 4s). After that the status isn’t retried on its own, but the next status change sends the latest status. Use GET /messages/{id} to catch up.