POST /api/v1/messages sends one transactional message and requires messages:write plus an Idempotency-Key of at most 200 characters. Use a new key for every new logical send.to: required; an email for email, or international phone for WhatsApp, SMS, and Smart.channel: email, whatsapp, sms, or smart; omitted means smart, including templates.message: plain text, required unless a saved template supplies content.template: workspace template public ID; variables provides its caller-facing values.subject / html / sender: email subject, optional HTML alternative, and optional active sender address. A template can supply the subject; an omitted sender uses the default active sender or available active sender.test: true to validate without sending or charging.purpose: use otp for phone verification messages so the OTP controls apply consistently.first_name, last_name, custom_fields: optional persistent contact profile data.list_id: an owned contact list; otherwise API Contacts is used.session: optional WhatsApp session selection.whatsapp_opt_in / whatsapp_opt_in_source: record genuine WhatsApp permission where applicable.1234567{ "to": "+2348012345678", "channel": "whatsapp", "message": "Hi Ada, order ORD-1042 has shipped.", "first_name": "Ada", "test": true }
12345678{ "to": "[email protected]", "channel": "email", "sender": "[email protected]", "template": "tpl_your_email_template", "variables": {"first_name": "Ada", "order_id": "ORD-1042"}, "test": true }
201 Created contains success, actual channel, status, cost, destination, and message_id when present. Template sends can include the template public ID.status: "validated" and zero cost. Live provider acceptance commonly returns sent/accepted status according to the channel. Follow delivery receipts to determine later outcomes.error_code before deciding whether to retry. Fix missing variables, sender readiness, or invalid destinations; honor rate-limit delays. After an ambiguous timeout, retry the exact request with its original idempotency key instead of creating another send.channel: "email"; omitting it for an email template still defaults to Smart.