Skip to main content

Notifications

Send an SMS when something happens to an order — booked, on its way, delivered.

Three objects, created independently and joined by a subscription:

ObjectDecides
Channelwhere a message goes. Carries the recipient.
Templatewhat it says.
Subscriptionwhen — ties a channel and a template to an event, and switches the whole thing on or off.

All three are owner-scoped: you only ever see and send for your own project.

The one thing to get right​

The recipient is a template, not a phone number

A channel's type_config.to is a Handlebars template, rendered against the order at send time. It is the single most common misconfiguration, because it fails silently:

  • an empty to renders to an empty recipient, and the message is dropped
  • a path that does not exist renders to an empty string — Handlebars does not raise

Neither produces an error you will see. If a notification "did not arrive", check to first.

A literal number is also valid, and does exactly what it says: every notification on that channel goes to that one number. That is occasionally what you want — an internal alert — and almost never what you want for a customer notification.

Creating a channel​

POST /v1/channels
Authorization: Bearer <token>

{
"name": "SMS to recipient",
"type": "zoopit-sms",
"type_config": {
"to": "{{delivery.phone_number}}"
},
"owner": "<project id>"
}

type is zoopit-sms. That is the only channel type today.

Template variables​

Rendered against the order, with its pickup and delivery locations resolved. The same variables work in to, in the template subject and in its content.

VariableIs
{{order.id}}The order's id — use it to build a tracking link
{{order.reference_id}}Your own reference, carried through unchanged
{{delivery.name}}Recipient name
{{delivery.phone_number}}Recipient phone number
{{delivery.street_address}}, {{delivery.zip_code}}, {{delivery.city}}Delivery address
{{pickup.name}}Sender / pickup point name
{{pickup.phone_number}}Sender phone number

delivery and pickup are the first place in the order's delivery.oneOf and pickup.oneOf. The full paths, such as {{order.delivery.oneOf.[0].location.phone_number}}, also work; use them with another index for an order that offers several places.

The recipient is read from delivery.oneOf, not from tasks

Planning works from the order's tasks, but templates read the order's delivery and pickup. An order created with tasks only renders {{delivery.phone_number}} empty, and gets no SMS. Put the recipient in delivery.oneOf as well — see Creating orders.

Creating a template​

POST /v1/templates

{
"name": "On its way",
"subject": "",
"content": "Your delivery is on its way. Follow it here: https://<your tracking host>/{{order.id}}",
"content_type": "text/plain",
"owner": "<project id>"
}

content_type is a free-text field; text/plain is the convention for SMS.

subject and content are joined with a newline into the SMS body. For SMS, leave subject empty and put everything in content — a two-line message costs the same as a one-line one only until it crosses a segment boundary.

Message length​

A GSM-7 SMS is 160 characters, or 153 per part once it splits into several. æ, ø and å are in the standard set and cost one character each, like any letter.

One character outside that set — a curly apostrophe ’, an em dash —, an emoji — switches the whole message to UCS-2, where the limit is 70 characters. If a message splits unexpectedly, look for a smart quote before you look at the length.

Creating a subscription​

POST /v1/subscriptions

{
"template_id": "<template id>",
"channel_id": "<channel id>",
"event": "orders.reserved",
"enabled": true,
"owner": "<project id>"
}

enabled: false keeps the wiring in place without sending. That is the safe way to set a flow up and review it before it reaches anyone.

Events​

EventFires when
orders.reservedAn order is created as RESERVED (the default), or an update sets status to RESERVED
orders.deliveredAn update sets task_status to DELIVERY_COMPLETED — normally the driver completing the delivery
orders.routedA route is saved that moves the order into ROUTED — for example a driver scans orders and creates the route in the Go app, or a dispatcher saves a route

An event fires on the change, not on the state: editing an order that is already RESERVED does not send the booking message again.

orders.routed fires once, when the route save moves the order into ROUTED. It does not fire when an already-routed order is moved to another route, or when an order reaches ROUTED any other way than a route save. Use it for a message that the delivery is on its way today. Subscriptions use the same channel, template and variables as the other events.

Before you switch one on​

Real messages cost real money and reach real people. A short checklist:

  1. Render to against a real order and confirm you get a number, not an empty string.
  2. Create the subscription with enabled: false, check the channel and template read the way you expect, then enable it.
  3. Count the characters of the finished message, including whatever {{order.reference_id}} expands to at its longest.
  4. Check who the recipient actually is. A literal number in to sends every message on that channel to one person.

What is not here yet​

There is no delivery log. The API cannot currently tell you what was sent, to whom, or when — so build your own record if you need one, and treat an enabled subscription as something to verify rather than something to assume.

There is also no test-send endpoint. Any send is a real one.

Messages are sent through Zoopit's SMS provider and sender. Sending through your own SMS gateway or provider account is coming.