Notify

Notify

Send one notification to one of your users and let HonkIO pick the channel: it tries push, SMS and email in order and stops at the first that delivers.

Quickstart

Notify sends one notification to one of your users and chooses the channel. You keep a recipient profile for each user, then call the API with the user's ID and the text. HonkIO tries the channels in order and stops at the first that delivers. Start with a test key: every channel runs in its own test mode.

  1. Save the person's contact details with PUT /v1/recipients/:user_id: a phone number, an email address and a language, whichever you have.
  2. Send with POST /v1/notify: the user_id and a text (and a subject when an email may be tried), or a template.
  3. Name your senders with from.sms and from.email. A channel that could reach the person but has no sender is refused with 422 NOTIFY_SENDER_REQUIRED.
  4. Read the outcome from the 202 answer, from GET /v1/notifications/:id or from the notification.delivered and notification.failed webhooks.

The contact details live in recipient profiles. A user with no profile can still be notified on push if they have an active device.

const { data: notification, error } = await honkio.notify.send({
  userId: 'user_1042',
  text: 'Your Acme code is 123456',
  subject: 'Your Acme code',
  from: { sms: '+1416XXXXXXX', email: 'hello@yourapp.ca' },
}, { idempotencyKey: 'login-code-1042-2026-11-03' })
if (error) throw new Error(error.message)
console.log(notification.id, notification.status, notification.channel)

Channels and order

By default Notify tries push, then SMS, then email, each at most once. Set channels to change the order or to leave a channel out.

A channel is skipped, with a reason on the attempt, when the person cannot be reached on it (no phone number, no email address, no active device), when it is off for the account or, in the default order, when the key may not send on it. A channel you name that the key may not send on is 403 FORBIDDEN. Contact points are read when you send: a channel the person could not be reached on then is skipped, even if a number, an address or a device is added before its turn.

An SMS or an email that its channel refuses falls through to the next channel, exactly where /v1/messages or /v1/emails would have refused it: no consent, an opt-out, a suppression, a sending limit, a pause or a low balance. A recipient profile is never consent.

text is the SMS, the push body and the email's plain text. subject and html refine the email. Or send a template with variables and each channel sends its own part, in the profile's language unless you pass language. See Templates.

const { data: notification, error } = await honkio.notify.send({
  userId: 'user_1042',
  template: 'order-shipped',
  variables: { orderId: '5521' },
  channels: ['sms', 'email'], // the order to try
  from: { sms: '+1416XXXXXXX', email: 'hello@yourapp.ca' },
}, { idempotencyKey: 'order-5521-shipped' })
if (error) throw new Error(error.message)
for (const attempt of notification.attempts) console.log(attempt.channel, attempt.outcome, attempt.reason)

Notifications are for codes, reminders, receipts and alerts. is_commercial defaults to false. Set it only for an email that promotes something: it needs CASL consent on file and adds the unsubscribe footer. An SMS keeps its own consent rules.

What delivered means

A notification is delivered when a channel accepts it: a push that at least one device (a phone or a browser) accepted, an SMS the carrier accepted, or an email the email service accepted. It stops there.

A failure reported after that does not fall through to another channel: a delivery receipt that says the SMS did not arrive, a bounce or a complaint. Accepted is not the same as seen, and Notify never sends one notification twice to make up for it.

A push that no device accepts falls through after a wait of up to a minute, as a push with an SMS fallback does. The notification is pending while a push or an email is being sent. An SMS the carrier refuses, or an email the email service refuses at once, falls through, and what was charged for it is refunded.

A channel with no verdict for an hour fails the notification, with the reason NOTIFY_STUCK on that channel and notify_stuck on the channels skipped after it. An email still queued (a pause, the daily cap) is left to finish, and if it then fails no other channel is tried.

An SMS whose carrier call timed out counts as delivered: the carrier may have taken it, so no other channel is tried.

Send an Idempotency-Key header. The same key returns the first notification, pending or not, and never sends or charges twice. The same key with a different body is 409 IDEMPOTENCY_KEY_REUSED.

Every notification lists its attempts in order. Each has a channel, an outcome (delivered, pending, skipped, refused or failed) and a reason where there is one: one of the lowercase reasons below, or the channel's own error code, such as NO_CONSENT, OPT_OUT_BLOCKED or EMAIL_SUPPRESSED.

no_active_deviceThe person has no active push device.
no_push_appThe account has no push app to send with.
no_phone_numberThe profile has no phone number.
no_emailThe profile has no email address.
no_senderThe channel had no sender to send from.
push_unavailableThe channel is off for the account.
push_disabledThe channel is off for the account.
email_unavailableThe channel is off for the account.
not_permittedThe key may not send on the channel.
template_channel_missingThe template has no part for the channel.
content_purgedThe notification's text was cleared before this channel was tried.
NOTIFY_STUCKA channel had no verdict for an hour.
notify_stuckSkipped because an earlier channel was stuck.

Pricing

Notify is included at no extra charge. A notification is billed at the delivering channel's normal price, and fall-through attempts aren't billed.

An SMS or email that was refused can still leave a failed message or email in your logs, and fire its own event, as the same send through /v1/messages or /v1/emails would.

Errors

Refusals come back as the usual error body. These codes belong to Notify; the others are in the error reference.

HTTPCodeMeaning
501NOTIFY_COMING_SOONNotify is not switched on for the platform yet.
404RECIPIENT_NOT_FOUNDNo profile with that user_id in the key's mode, and no active push device for it.
422NOTIFY_SENDER_REQUIREDA channel that can reach the person has no sender. details.channel names it.
422NOTIFY_CONTENT_REQUIREDNothing to send: give text (and a subject to email), or a template. details.field names what is missing.
422TEMPLATE_CHANNEL_MISSINGThe template has no part for a channel you named, or for any channel that can be tried.
422NOTIFY_CONTENT_TOO_LARGEWhat the notification would send on the channels that can reach the person, rendered, is over 2 MB. details.bytes and details.limit give the sizes.
409RECIPIENT_LIMIT_REACHEDThe account already holds its maximum of profiles in this mode: 1,000,000 live, 1,000 test. details.limit gives it.
409IDEMPOTENCY_KEY_REUSEDThe Idempotency-Key was used for a different notification.
403FORBIDDENThe key lacks notifications write or recipients read, or may not send on any channel, or on one you named.

Webhooks

notification.delivered fires when a channel delivered the notification and notification.failed when no channel did. Both carry the notification in data, with no message text.

The data is id, user_id, status, channel (the one that delivered), message_id, email_id, push_id (while push is on), attempts (each with channel, outcome and reason) and the livemode flag, as in the envelope.

The SMS, email or push behind a notification also fires its own events. Subscribe to the notification events when you want one answer per notification.