Quickstart: send your first notification
In this guide you send a message, read the response and check whether it was delivered. The examples use email, but the request is the same for SMS and WhatsApp — only channel changes.
Before you start
You'll need:
- A Notify API key, stored in the
NOTIFY_API_KEYenvironment variable. - A template in your account. The examples use one called
order_confirmationthat expects thenameandorder_idvariables.
Authentication
Every request carries your API key in the Authorization header as a Bearer token. The key identifies your account, so never send a customer ID in the body.
Keep the key out of browsers and mobile apps: call Notify from your backend.
Authorization: Bearer $NOTIFY_API_KEYSend a message
Make a POST /v1/messages request with the channel, the recipient and the template key. If you pass a locale, Notify picks the template version in that language.
curl https://api.notify.com.mx/v1/messages \
-H "Authorization: Bearer $NOTIFY_API_KEY" \
-H "Idempotency-Key: order-1042-confirmation" \
-H "Content-Type: application/json" \
-d '{
"channel": "email",
"recipient": { "address": "ana@example.com", "locale": "es" },
"template_key": "order_confirmation",
"variables": { "name": "Ana", "order_id": "1042" }
}'import os
import requests
response = requests.post(
"https://api.notify.com.mx/v1/messages",
headers={
"Authorization": f"Bearer {os.environ['NOTIFY_API_KEY']}",
"Idempotency-Key": "order-1042-confirmation",
},
json={
"channel": "email",
"recipient": {"address": "ana@example.com", "locale": "es"},
"template_key": "order_confirmation",
"variables": {"name": "Ana", "order_id": "1042"},
},
timeout=10,
)
response.raise_for_status() # 202 Accepted
message = response.json()
print(message["id"], message["status"])<?php
$ch = curl_init('https://api.notify.com.mx/v1/messages');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('NOTIFY_API_KEY'),
'Idempotency-Key: order-1042-confirmation',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'channel' => 'email',
'recipient' => ['address' => 'ana@example.com', 'locale' => 'es'],
'template_key' => 'order_confirmation',
'variables' => ['name' => 'Ana', 'order_id' => '1042'],
]),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE); // 202
$message = json_decode($body, true);
echo $message['id'] . ' ' . $message['status'];const response = await fetch('https://api.notify.com.mx/v1/messages', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NOTIFY_API_KEY}`,
'Idempotency-Key': 'order-1042-confirmation',
'Content-Type': 'application/json',
},
body: JSON.stringify({
channel: 'email',
recipient: { address: 'ana@example.com', locale: 'es' },
template_key: 'order_confirmation',
variables: { name: 'Ana', order_id: '1042' },
}),
});
if (response.status !== 202) throw new Error(await response.text());
const message = await response.json();
console.log(message.id, message.status);Reading the response: 202, not 200
Notify returns 202 Accepted as soon as the request is validated and queued. That means the message was accepted — not that it was delivered. Delivery happens in the background, with retries if a provider fails.
Keep the id from the response: you'll use it to check the real status.
HTTP/1.1 202 Accepted
{
"id": "6f1c2a4e-9b7d-4c1e-8a35-2d0f5b9e7c11",
"status": "accepted",
"channel": "email",
"template_key": "order_confirmation",
"resolved_locale": "es",
"created_at": "2026-10-05T17:42:08Z"
}Check whether it arrived
GET /v1/messages/{id} returns the current status plus the history of attempts per provider.
curl https://api.notify.com.mx/v1/messages/6f1c2a4e-9b7d-4c1e-8a35-2d0f5b9e7c11 \
-H "Authorization: Bearer $NOTIFY_API_KEY"{
"id": "6f1c2a4e-9b7d-4c1e-8a35-2d0f5b9e7c11",
"status": "delivered",
"channel": "email",
"template_key": "order_confirmation",
"resolved_locale": "es",
"created_at": "2026-10-05T17:42:08Z",
"delivered_at": "2026-10-05T17:42:11Z",
"attempts": [
{
"attempt_number": 1,
"provider": "ses",
"status": "sent",
"attempted_at": "2026-10-05T17:42:09Z"
}
]
}Message statuses
| Status | What it means |
|---|---|
accepted | Accepted and queued; not sent yet. |
processing | A worker is handing it to the provider. |
sent | The provider accepted it. Delivery isn't confirmed yet. |
delivered | The provider confirmed delivery to the recipient. |
failed | All retries were exhausted. Check attempts for the reason. |
blocked | Not sent because the recipient opted out of that channel. |
Retry without duplicates
Networks fail. If you don't get a response, retry with the same Idempotency-Key header: Notify returns the original message and never sends twice.
Use a key that identifies the business event — e.g. order-1042-confirmation — not a random value per attempt.
Errors
Errors always include a stable code for your code to branch on and a readable message for your logs. A 422 means the request is well-formed but can't be processed — for example, a template variable is missing. On a 429, wait the number of seconds in the Retry-After header.
HTTP/1.1 422 Unprocessable Entity
{
"code": "missing_required_variable",
"message": "Template order_confirmation requires the order_id variable.",
"details": { "variable": "order_id" }
}Next steps
Head to the full reference to create multilingual templates, list messages and see every field in the API.