POST /v1/messages reaches one person. You say who, what, and — through a route —
which channels to try in which order and what counts as getting there on each. OMNI tries
the first; if it can’t take the message, doesn’t deliver it in time, or the person
doesn’t read it in time, the next one goes.
202 Accepted with the message as it stands — usually its first attempt
already made. What happens next arrives as webhooks, and
GET /v1/messages/{message_id} tells the whole story at any time.
Routes: which channels, in what order
A route is a list of up to six steps. Each step names a channel, what counts as done on it, and how long to wait:within is in seconds, from 30 to 7 days; 15 minutes when left out. A step can only wait
for what its channel reports — asking SMS for read is refused.
Save routes with POST /v1/routes and name them in a message, or give steps in the
message itself. One route can be the default, used when a message names none.
Without a default, a message is tried on your channels in their usual order (WhatsApp,
then SMS), each done when delivered, within 15 minutes.
Calls as a step
When your account has Voice, a route can end with a call: WhatsApp, then SMS, then ring them into an IVR, done when answered.POST /v1/calls — screened for
consent the same way — and the attempt carries its call id. A message that names no
route is never turned into a phone call: calls happen only when a route asks for them.
How a step hands over
A step is refused, for example, when WhatsApp’s 24-hour window is closed and no
approved template was given (
window_closed), when an SMS’s text fills in none of your
registered DLT templates (no_dlt_match), when nothing was written for that channel
(no_content), when the channel is off (channel_off), or when the provider says no
(provider_refused).
If a channel gets there after its step has timed out, that is recorded on its attempt
— but nothing is sent again.
What to send: templates and content
A unified template is one message written once per channel, with{{values}}:
"template": "order_shipped" and its "values". A message missing a value
the template needs is refused before anything is sent. You can also give content per
channel instead of a template — or as well, to change one part of it:
Consent and quiet hours
Every attempt is screened like any message OMNI sends:- Someone who asked never to be contacted, or opted out of everything, is never
tried on any channel: the message fails with
not_permitted. - A channel they opted out of is skipped: that step is refused with
not_permitted. - During their quiet hours the message waits (
postponed) and is tried when they end. "purpose": "marketing"applies marketing consent;transactional(the default) applies the rules for messages people expect.
How a message ends
Each attempt points at the message it sent on its channel (
cm_…), whose own deliveries
arrive as channel_message.* events.
Charging. Every attempt a channel actually took is a message sent on that channel
and is charged like one. Refused steps cost nothing. A test key’s messages reach no
channel and cost nothing.
Test keys
With anff_test_… key, a message succeeds at once on its first step with the code
test, and nothing reaches any phone — so you can build the whole flow, webhooks
included, before going live.