Callbacks (Webhook)
Webhook callbacks for Standard and Transactional Failover — payload shape, strategies, retry policy, and flowKind.
Callbacks are HTTP POST webhooks Routee sends to your URL when a Failover flow completes a step or reaches a terminal outcome. They apply to Standard and Transactional Failover (POST /failover and POST /failover/transactional).
Failover OTP uses a separate service. OTP webhooks use optional
callback.urlon create and a different payload shape — see Send a Failover OTP message.
Enable callbacks on create
Include a callback object when you create a flow:
{
"flow": [ ... ],
"callback": {
"strategy": "OnStep",
"url": "https://your-server.com/failover-webhook"
}
}| Field | Required | Values |
|---|---|---|
callback.url | Yes | HTTPS (or HTTP) endpoint that accepts POST with Content-Type: application/json |
callback.strategy | Yes | OnStep or OnCompletion |
Both strategies use the same JSON payload shape (below). They differ only in when Routee sends it.
When callbacks are sent
The Failover service publishes a callback task when:
- A step completes and failover advances to the next channel (
FlowStepChangedEvent), or - The flow reaches a terminal outcome —
SucceededorFailed(FlowStatusChangedEvent).
| Strategy | Sent when |
|---|---|
| OnStep | On every step transition and when the flow reaches Succeeded or Failed. Mid-flow payloads usually have root status: "InProgress". |
| OnCompletion | Only when the flow reaches a terminal status — Succeeded or Failed. No callback while the flow is still progressing through channels. |
What is in messages[]: Only completed steps at the time of the callback. The service loads tracking from Routee for each completed step and sorts messages by order, then by type (SMS parts by part, etc.). Steps still in progress are not included until they complete.
If Routee tracking cannot be loaded for a step, a minimal message object is still sent (ids, to, order, failoverOnStatuses, and type) so your webhook is not empty.
HTTP delivery
| Item | Detail |
|---|---|
| Method | POST |
| Headers | Content-Type: application/json |
| Body | JSON object (same structure as GET /failover/tracking/{trackingId} for Standard / Transactional) |
| Success | Your endpoint should respond HTTP 200 OK promptly (Routee callbacks platform uses a short HTTP client timeout, typically 2 seconds) |
| Retries | On failure, the Routee callbacks service retries with increasing delays (see below) |
Example payload (Standard — Viber then SMS)
{
"trackingId": "30830fad-21ed-49b0-89d0-bbada2fd5640",
"flowKind": "standard",
"messages": [
{
"type": "Viber",
"trackingId": "0a047b46-a4dd-4ca8-b817-a6a134dd1234",
"to": "+306912345678",
"from": "Routeetest",
"country": "GR",
"ttl": 10,
"expireOnDelivery": false,
"expiredOnDeliveryAt": null,
"status": {
"name": "DELIVERED",
"reason": {
"detailedStatus": "Delivered",
"description": "The message was delivered to its recipient."
},
"updatedDate": "2019-07-05T10:28:44.996Z"
},
"body": {
"text": "This is a test, please ignore!",
"imageUrl": "https://example.com/image.png",
"action": {
"caption": "Go",
"targetUrl": "https://www.routee.net/"
}
},
"label": "labelName",
"failoverOnStatuses": ["EXPIRED", "FAILED", "UNDELIVERED"],
"order": 1,
"inboundUrl": "https://your-server.com/inbound",
"price": 0.035,
"createdAt": "2019-07-05T10:28:44.566Z"
},
{
"type": "Sms",
"messageId": "82a91dc3-e842-41eb-86e5-07d55eb51234",
"smsId": "1274d238-38b7-4c56-b49a-9b70d25c1234",
"to": "+306912345678",
"from": "Routee",
"country": "GR",
"operator": "Vodafone",
"ttl": 15,
"status": {
"name": "Delivered",
"reason": {
"detailedStatus": "Delivered",
"description": "The SMS was sent and it was delivered to its recipient."
},
"updatedDate": "2019-07-05T10:28:49Z"
},
"body": "Hello from failover",
"price": 0.036,
"label": "labelName",
"order": 2,
"part": 1,
"parts": 1,
"latency": 4,
"failoverOnStatuses": ["Undelivered", "Failed"],
"createdAt": "2019-07-05T10:28:45.227Z"
}
],
"terminationChannel": "Sms",
"status": "Succeeded",
"statusCode": null,
"totalPrice": 0.071,
"originatingService": "failover",
"applicationId": "5b223c27e4b04b9cdaa71234",
"createdAt": "2019-07-05T10:28:44.44Z",
"updatedAt": "2019-07-05T10:28:49.634Z"
}Notes on the example
- Root
statusin callbacks isInProgress,Succeeded, orFailed— notQueued(callbacks fire after the flow has started processing). originatingServiceis always the lowercase stringfailover.statusCodeis present only when rootstatusisFailed— values"0"–"3"(see Failover Statuses).- Each message includes
type:Sms,Viber, orVoice. - Viber:
body.action(notviberAction);ttlis in minutes (converted from seconds in tracking). Optionalbody.viberFile,body.viberVideofor rich content. - SMS:
bodyis a string. Multipart SMS may produce multiplemessages[]entries with the sameorderand differentpart/smsId. - Transactional flows (
flowKind: transactional): Viber entries usetemplateId,templateLang,templateParams,deliveryScope,seqinstead of promotionalbody.text/ media.
Payload reference — root level
| KEY | DESCRIPTION |
|---|---|
| trackingId | Failover flow tracking id (same as create response). |
| flowKind | standard (POST /failover) or transactional (POST /failover/transactional). Omitted on older flows → treat as standard. |
| messages | Array of completed step messages, sorted by order. |
| terminationChannel | Channel of the last executed step when the flow completed: Sms, Viber, or Voice. May be null if not set. |
| status | InProgress, Succeeded, or Failed. |
| statusCode | Only when status is Failed. String "0"–"3" — see Failover Statuses. |
| totalPrice | Sum of price on messages in this payload (where price is available). |
| originatingService | Always failover. |
| applicationId | Routee application id that created the flow. |
| createdAt | Flow creation time (ISO 8601). |
| updatedAt | Time of the flow state update that triggered this callback (ISO 8601). |
Payload reference — all message types
| KEY | DESCRIPTION |
|---|---|
| type | Sms, Viber, or Voice. |
| to | Recipient. |
| from | Sender id (SMS/Voice) or sender name (Viber). |
| country | Recipient country (ISO 3166-1 alpha-2) when available. |
| order | Step order in the flow (1, 2, …). |
| failoverOnStatuses | Statuses that trigger the next step for this channel (echoed from your request; casing may vary by channel). |
| status | Delivery status object for this step. |
| status.name | Channel-specific status name — see Failover Statuses. |
| status.reason | Optional. SMS/Viber: object with detailedStatus, description. Voice: reason and description are strings on status. |
| status.updatedDate | Last status change (ISO 8601). |
| price | Cost of this message when available. |
| createdAt | When this channel tracking was created (ISO 8601). |
Viber only
| KEY | DESCRIPTION |
|---|---|
| trackingId | Viber message tracking id. |
| ttl | TTL in minutes (0.5–1440; default 10). |
| body | Content object: text, imageUrl, action, viberFile, viberVideo. |
| body.action.caption | Button label (1–30 characters). |
| body.action.targetUrl | Button URL. |
| inboundUrl | Inbound Viber messages callback URL if set on create. |
| label | Optional message label. |
| expireOnDelivery | If true, Delivered may become Expired after TTL with no Seen. |
| expiredOnDeliveryAt | When expire-on-delivery fired (ISO 8601). |
| templateId | Transactional only — BM template UUID. |
| templateLang | Transactional only — template language. |
| templateParams | Transactional only — placeholder map. |
| deliveryScope | Transactional only — PRIMARY_DEVICE or ALL_DEVICES. |
| seq | Transactional only — sequence number from request. |
SMS only
| KEY | DESCRIPTION |
|---|---|
| body | SMS text (string, not an object). |
| messageId | SMS part / tracking id for this part. |
| smsId | Parent SMS message id (shared across parts). |
| part | Part number (multipart). |
| parts | Total parts. |
| operator | Mobile operator when available. |
| latency | Delivery latency in milliseconds. |
| ttl | TTL in minutes (1–1440; default 1200). |
| label | Optional label. |
Voice only
| KEY | DESCRIPTION |
|---|---|
| messageId | Voice message id. |
| conversationTrackingId | Voice conversation tracking id. |
| status.reason | Extra reason (string). |
| status.description | Human-readable description (string). |
| duration | Call duration (seconds). |
| chargeInterval | Billing interval (seconds). |
| answeredBy | Machine detection result when enabled (e.g. HUMAN). |
| fileURL | Reserved; typically null in current implementations. |
| failoverOnStatuses | e.g. Busy, NoAnswer (defaults) — may include Failed, Unsent, Terminated if configured. |
Callback strategy (summary)
| Strategy | Use when |
|---|---|
| OnStep | You want updates after each channel attempt (including mid-flow InProgress) and on final outcome. |
| OnCompletion | You only care about the final Succeeded / Failed result. |
Callback retry policy
Your endpoint must respond with HTTP 200 OK within the callbacks platform HTTP timeout (typically 2 seconds). Otherwise Routee closes the connection and retries delivery via the shared callbacks service.
Documented retry schedule (up to 12 retries over 24 hours):
| Retry | Delay after previous attempt |
|---|---|
| 1st | 30 sec |
| 2nd | 1 min |
| 3rd | 2 min |
| 4th | 5 min |
| 5th | 10 min |
| 6th | 15 min |
| 7th | 30 min |
| 8th | 1 hour |
| 9th | 2 hours |
| 10th | 4 hours |
| 11th | 8 hours |
| 12th | 24 hours |
After retries are exhausted, delivery stops for that callback chain. Design your handler to be idempotent — the same flow update may be POSTed more than once.
Related documentation
| Topic | Page |
|---|---|
Flow & channel statuses, statusCode | Failover Statuses |
| Which API to use | Failover endpoints |
| Tracking API (same payload shape) | Resource Overview |
| Transactional create | How to send a transactional Failover message |
Updated 13 days ago

