WhatsApp API
OTP, free-form messages, and approved Utility templates (e.g. morisbox_invite). Templates are managed via /v1/whatsapp/templates; OTP stays on /v1/whatsapp/otp/send.
OTP
/v1/otp/sendPrimary OTP send. You pass to and optionally code - SEED handles WhatsApp delivery, SMS fallback, and templates. Default delivery_mode=auto tries WhatsApp first, then SMS with the same code. Omit code to let SEED generate one. Use POST /v1/otp/verify to validate.
| Name | Type | Description | Required |
|---|---|---|---|
to | string | Recipient phone number in international format. | Yes |
reference | string | Optional client reference for later verification. | No |
code | string | Optional 4 to 8 digit code you generated. When omitted, SEED generates one automatically. | No |
delivery_mode | string | auto (default: WhatsApp then SMS), whatsapp, or sms. | No |
sms_sender | string | Optional approved SMS sender used on SMS fallback. | No |
template_name | string | Advanced only: override the authentication template configured on the WhatsApp account. Not required for standard integrations. | No |
template_language | string | Advanced only: override template locale when using template_name. | No |
code_length | number | Optional OTP length between 4 and 8 digits when SEED generates the code. | No |
request_messaging_consent | boolean | When true (default), sends a Yes/No consent prompt to open a billable 24h messaging window after OTP. | No |
- Create an API key with otp:send scope (sms:send also works).
- POST /v1/otp/send with to (and optional code). No template or channel field required.
- SEED tries WhatsApp first; if needed, the same code is sent by SMS.
- Read channel and fallback_used in the response (provider is always seed).
- Poll GET /v1/otp/{challenge_id} for status, attempts, and expires_at.
- POST /v1/otp/verify when the user enters the code.
{
"to": "+22890000000",
"code": "482917",
"reference": "login-1001",
"delivery_mode": "auto",
"request_messaging_consent": false
}{
"challenge_id": "otp_123",
"channel": "sms",
"provider": "seed",
"to": "+22890000000",
"reference": "login-1001",
"status": "sent",
"provider_message_id": "sms-abc123",
"expires_at": "2026-05-14T12:10:00Z",
"fallback_used": true,
"whatsapp_available": false
}/v1/whatsapp/otp/sendWhatsApp-only delivery for a client-supplied code (no SMS fallback). Pass to + code only. Prefer POST /v1/otp/send with code for automatic WhatsApp → SMS.
| Name | Type | Description | Required |
|---|---|---|---|
to | string | Recipient phone number in international format. | Yes |
code | string | 4 to 8 digit OTP generated by your application. | Yes |
client_id | string | Optional client reference echoed in the response. | No |
reference | string | Optional reference stored with the OTP challenge. | No |
template_name | string | Advanced only: override the account authentication template. Not required for standard integrations. | No |
template_language | string | Advanced only: override template locale when using template_name. | No |
- Generate the OTP in your backend.
- POST /v1/whatsapp/otp/send with to and code (no template required).
- Or use POST /v1/otp/send with code for auto WhatsApp → SMS fallback.
- Poll GET /v1/otp/{challenge_id} for delivery status.
{
"to": "+22890000000",
"code": "482917",
"client_id": "user-1001"
}{
"sent": true,
"to": "+22890000000",
"wamid": "wamid.HBg...",
"challenge_id": "otp_123",
"client_id": "user-1001",
"reference": null,
"provider": "seed"
}/v1/otp/verifyVerify an OTP challenge by challenge_id, reference, or recipient phone.
| Name | Type | Description | Required |
|---|---|---|---|
code | string | OTP entered by the user. | Yes |
challenge_id | string | Challenge id returned by /v1/otp/send. | Yes |
reference | string | Optional client reference used during send. | No |
to | string | Optional recipient phone used during send. | No |
{
"challenge_id": "otp_123",
"code": "482913"
}{
"verified": true,
"challenge_id": "otp_123",
"status": "verified",
"verified_at": "2026-05-14T12:01:00Z"
}/v1/otp/{challenge_id}Check OTP challenge status without exposing the code. Status values include sent, delivered, verified, expired, and failed. Compare expires_at with the current time to detect expiry before calling verify.
| Name | Type | Description | Required |
|---|---|---|---|
challenge_id | path | Challenge identifier returned by /v1/otp/send or /v1/whatsapp/otp/send. | Yes |
No request body.
{
"challenge_id": "otp_123",
"channel": "whatsapp",
"provider": "seed",
"to": "+22890000000",
"reference": "login-1001",
"status": "sent",
"provider_message_id": "wamid.HBg...",
"attempts": 0,
"max_attempts": 5,
"expires_at": "2026-05-14T12:10:00Z",
"verified_at": null,
"created_at": "2026-05-14T12:00:00Z"
}WhatsApp messaging
/v1/whatsapp/messagesSend WhatsApp free-form text or an approved Utility/Marketing template. Provide either text or template_name (+ language). When both are present, template mode wins. Billing: session (free inside 24h consent) or direct (country price_whatsapp_message).
| Name | Type | Description | Required |
|---|---|---|---|
to | string | Recipient WhatsApp number. | Yes |
text | string | Free-form body (required unless template_name is set). | No |
template_name | string | Approved template name, e.g. morisbox_invite. Enables template mode. | No |
template_language | string | Template language code (default fr). | No |
template_params | string[] | Body variables in order ({{1}}, {{2}}, …). | No |
template_id | string | Optional provider template id. | No |
components | object[] | Optional Meta-like components override. | No |
billing_mode | string | direct or session. | Yes |
reference | string | Optional client reference. | No |
client_id | string | Optional account/client id from the integrator. | No |
country | string | Optional ISO country code; auto-detected from phone when omitted. | No |
- Free-form: send OTP with consent, then billing_mode=session inside the 24h window, or billing_mode=direct.
- Template (e.g. MorisBox invite): POST with template_name=morisbox_invite, template_language=fr, template_params=[sender_label] — no text required.
- Template must be APPROVED on the linked WABA; otherwise 409 template_not_approved.
- API key needs whatsapp:send scope.
- Subscribe to whatsapp.status webhooks for delivery receipts.
{
"to": "+22890000000",
"template_name": "morisbox_invite",
"template_language": "fr",
"template_params": [
"LeadBugs"
],
"billing_mode": "direct",
"reference": "morisbox-invite-msg-123"
}{
"id": "wm_123",
"channel": "whatsapp",
"provider": "seed",
"billing_mode": "direct",
"to": "22890000000",
"country": "TG",
"status": "submitted",
"template": "morisbox_invite",
"language": "fr",
"billing_kind": "whatsapp_utility",
"charged_credits": 2,
"session_id": null,
"created_at": "2026-05-14T12:00:00Z"
}/v1/whatsapp/templatesList WhatsApp templates known locally and/or on the linked WABA (360dialog). Filter with name, language, status.
| Name | Type | Description | Required |
|---|---|---|---|
name | query | Filter by template name. | No |
language | query | Filter by language code. | No |
status | query | Filter by status (APPROVED, PENDING, REJECTED). | No |
No request body.
{
"templates": [
{
"name": "morisbox_invite",
"language": "fr",
"category": "UTILITY",
"status": "APPROVED",
"provider_template_id": "…",
"has_url_button": true,
"body_param_count": 1
}
]
}/v1/whatsapp/templatesDeclare a template on SEED and optionally submit it to 360dialog/Meta (submit=true).
| Name | Type | Description | Required |
|---|---|---|---|
name | string | Template name (Meta). | Yes |
language | string | Language code (default fr). | No |
category | string | UTILITY | MARKETING | AUTHENTICATION. | No |
body | string | Body text with optional {{1}} variables. | Yes |
footer | string | Optional footer. | No |
buttons | object[] | Optional buttons (URL, PHONE_NUMBER, QUICK_REPLY). | No |
submit | boolean | Submit to 360dialog/Meta (default true). | No |
{
"name": "morisbox_invite",
"language": "fr",
"category": "UTILITY",
"body": "Vous avez un message en attente sur MorisBox.\n\nExpéditeur : {{1}}\n\nOuvrez MorisBox pour le consulter.",
"buttons": [
{
"type": "URL",
"text": "Ouvrir MorisBox",
"url": "https://morisbox.com"
}
],
"submit": true
}{
"name": "morisbox_invite",
"language": "fr",
"status": "PENDING",
"provider_template_id": "…",
"message": "Submitted to 360dialog/Meta for approval"
}/v1/whatsapp/templates/{name}Template detail and latest status (PENDING / APPROVED / REJECTED) plus rejection_reason when available.
| Name | Type | Description | Required |
|---|---|---|---|
name | path | Template name. | Yes |
language | query | Language code (default fr). | No |
No request body.
{
"name": "morisbox_invite",
"language": "fr",
"category": "UTILITY",
"status": "APPROVED",
"has_url_button": true,
"body_param_count": 1
}/v1/whatsapp/templates/{name}/syncRe-sync template status from 360dialog/Meta into the local store.
| Name | Type | Description | Required |
|---|---|---|---|
name | path | Template name. | Yes |
language | query | Language code (default fr). | No |
No request body.
{
"name": "morisbox_invite",
"language": "fr",
"status": "APPROVED",
"message": "Template is approved and ready for delivery."
}/v1/whatsapp/sessionsList 24h WhatsApp consent sessions (pending, active, declined).
No request body.
[
{
"id": "ws_123",
"recipient": "22890000000",
"country": "TG",
"consent_status": "yes",
"status": "active",
"billed_credits": 5,
"expires_at": "2026-05-15T12:00:00Z",
"otp_challenge_id": "otp_123"
}
]/v1/whatsapp/sessions/{session_id}Fetch one WhatsApp consent session by id.
| Name | Type | Description | Required |
|---|---|---|---|
session_id | path | Session identifier returned by OTP send or session list. | Yes |
No request body.
{
"id": "ws_123",
"recipient": "22890000000",
"country": "TG",
"consent_status": "yes",
"status": "active",
"billed_credits": 5,
"expires_at": "2026-05-15T12:00:00Z",
"consent_at": "2026-05-14T12:02:00Z"
}/v1/whatsapp/messagesList sent WhatsApp messages and charged credits.
No request body.
[
{
"id": "wm_123",
"channel": "whatsapp",
"billing_mode": "session",
"to": "22890000000",
"country": "TG",
"status": "submitted",
"charged_credits": 0,
"session_id": "ws_123"
}
]WhatsApp inbound
/v1/inbound-notificationsList inbound SMS or WhatsApp messages matched to the authenticated account. Responses always use provider=seed; use the channel field to distinguish SMS from WhatsApp. Filter with provider=whatsapp or provider=ngh.
| Name | Type | Description | Required |
|---|---|---|---|
provider | query | Optional filter: ngh for SMS or whatsapp for WhatsApp (provider=seed returns all). A whatsapp:receive-only key must filter with provider=whatsapp. | No |
No request body.
[
{
"id": "in_123",
"provider": "seed",
"channel": "whatsapp",
"sender": "22890000000",
"recipient": "15550000000",
"message": "Yes",
"received_at": "2026-05-14T12:02:00Z",
"created_at": "2026-05-14T12:02:00Z"
}
]/v1/inbound-notifications/{notification_id}Fetch one inbound WhatsApp or SMS message with the raw provider payload.
| Name | Type | Description | Required |
|---|---|---|---|
notification_id | path | Inbound notification id. | Yes |
No request body.
{
"id": "in_123",
"provider": "seed",
"channel": "whatsapp",
"sender": "22890000000",
"message": "Yes",
"raw_json": "{\"message\":{\"type\":\"interactive\",\"button_reply\":{\"id\":\"consent_yes\"}}}",
"created_at": "2026-05-14T12:02:00Z"
}