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

POST/v1/otp/send

Primary 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.

AuthX-API-Key (otp:send or sms:send)
Parameters
NameTypeDescriptionRequired
tostringRecipient phone number in international format.Yes
referencestringOptional client reference for later verification.No
codestringOptional 4 to 8 digit code you generated. When omitted, SEED generates one automatically.No
delivery_modestringauto (default: WhatsApp then SMS), whatsapp, or sms.No
sms_senderstringOptional approved SMS sender used on SMS fallback.No
template_namestringAdvanced only: override the authentication template configured on the WhatsApp account. Not required for standard integrations.No
template_languagestringAdvanced only: override template locale when using template_name.No
code_lengthnumberOptional OTP length between 4 and 8 digits when SEED generates the code.No
request_messaging_consentbooleanWhen true (default), sends a Yes/No consent prompt to open a billable 24h messaging window after OTP.No
Flow
  1. Create an API key with otp:send scope (sms:send also works).
  2. POST /v1/otp/send with to (and optional code). No template or channel field required.
  3. SEED tries WhatsApp first; if needed, the same code is sent by SMS.
  4. Read channel and fallback_used in the response (provider is always seed).
  5. Poll GET /v1/otp/{challenge_id} for status, attempts, and expires_at.
  6. POST /v1/otp/verify when the user enters the code.
Request samplePOST /v1/otp/send
{
  "to": "+22890000000",
  "code": "482917",
  "reference": "login-1001",
  "delivery_mode": "auto",
  "request_messaging_consent": false
}
Response sample200 OK
{
  "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
}
POST/v1/whatsapp/otp/send

WhatsApp-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.

AuthX-API-Key (otp:send or sms:send)
Parameters
NameTypeDescriptionRequired
tostringRecipient phone number in international format.Yes
codestring4 to 8 digit OTP generated by your application.Yes
client_idstringOptional client reference echoed in the response.No
referencestringOptional reference stored with the OTP challenge.No
template_namestringAdvanced only: override the account authentication template. Not required for standard integrations.No
template_languagestringAdvanced only: override template locale when using template_name.No
Flow
  1. Generate the OTP in your backend.
  2. POST /v1/whatsapp/otp/send with to and code (no template required).
  3. Or use POST /v1/otp/send with code for auto WhatsApp → SMS fallback.
  4. Poll GET /v1/otp/{challenge_id} for delivery status.
Request samplePOST /v1/whatsapp/otp/send
{
  "to": "+22890000000",
  "code": "482917",
  "client_id": "user-1001"
}
Response sample200 OK
{
  "sent": true,
  "to": "+22890000000",
  "wamid": "wamid.HBg...",
  "challenge_id": "otp_123",
  "client_id": "user-1001",
  "reference": null,
  "provider": "seed"
}
POST/v1/otp/verify

Verify an OTP challenge by challenge_id, reference, or recipient phone.

AuthX-API-Key (otp:verify, sms:send, or account:read)
Parameters
NameTypeDescriptionRequired
codestringOTP entered by the user.Yes
challenge_idstringChallenge id returned by /v1/otp/send.Yes
referencestringOptional client reference used during send.No
tostringOptional recipient phone used during send.No
Request samplePOST /v1/otp/verify
{
  "challenge_id": "otp_123",
  "code": "482913"
}
Response sample200 OK
{
  "verified": true,
  "challenge_id": "otp_123",
  "status": "verified",
  "verified_at": "2026-05-14T12:01:00Z"
}
GET/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.

AuthX-API-Key (otp:verify, sms:send, or account:read)
Parameters
NameTypeDescriptionRequired
challenge_idpathChallenge identifier returned by /v1/otp/send or /v1/whatsapp/otp/send.Yes
Request sampleGET /v1/otp/{challenge_id}
No request body.
Response sample200 OK
{
  "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

POST/v1/whatsapp/messages

Send 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).

AuthX-API-Key (whatsapp:send or sms:send)
Parameters
NameTypeDescriptionRequired
tostringRecipient WhatsApp number.Yes
textstringFree-form body (required unless template_name is set).No
template_namestringApproved template name, e.g. morisbox_invite. Enables template mode.No
template_languagestringTemplate language code (default fr).No
template_paramsstring[]Body variables in order ({{1}}, {{2}}, …).No
template_idstringOptional provider template id.No
componentsobject[]Optional Meta-like components override.No
billing_modestringdirect or session.Yes
referencestringOptional client reference.No
client_idstringOptional account/client id from the integrator.No
countrystringOptional ISO country code; auto-detected from phone when omitted.No
Flow
  1. Free-form: send OTP with consent, then billing_mode=session inside the 24h window, or billing_mode=direct.
  2. Template (e.g. MorisBox invite): POST with template_name=morisbox_invite, template_language=fr, template_params=[sender_label] — no text required.
  3. Template must be APPROVED on the linked WABA; otherwise 409 template_not_approved.
  4. API key needs whatsapp:send scope.
  5. Subscribe to whatsapp.status webhooks for delivery receipts.
Request samplePOST /v1/whatsapp/messages
{
  "to": "+22890000000",
  "template_name": "morisbox_invite",
  "template_language": "fr",
  "template_params": [
    "LeadBugs"
  ],
  "billing_mode": "direct",
  "reference": "morisbox-invite-msg-123"
}
Response sample200 OK
{
  "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"
}
GET/v1/whatsapp/templates

List WhatsApp templates known locally and/or on the linked WABA (360dialog). Filter with name, language, status.

AuthX-API-Key (whatsapp:send)
Parameters
NameTypeDescriptionRequired
namequeryFilter by template name.No
languagequeryFilter by language code.No
statusqueryFilter by status (APPROVED, PENDING, REJECTED).No
Request sampleGET /v1/whatsapp/templates
No request body.
Response sample200 OK
{
  "templates": [
    {
      "name": "morisbox_invite",
      "language": "fr",
      "category": "UTILITY",
      "status": "APPROVED",
      "provider_template_id": "…",
      "has_url_button": true,
      "body_param_count": 1
    }
  ]
}
POST/v1/whatsapp/templates

Declare a template on SEED and optionally submit it to 360dialog/Meta (submit=true).

AuthX-API-Key (whatsapp:send)
Parameters
NameTypeDescriptionRequired
namestringTemplate name (Meta).Yes
languagestringLanguage code (default fr).No
categorystringUTILITY | MARKETING | AUTHENTICATION.No
bodystringBody text with optional {{1}} variables.Yes
footerstringOptional footer.No
buttonsobject[]Optional buttons (URL, PHONE_NUMBER, QUICK_REPLY).No
submitbooleanSubmit to 360dialog/Meta (default true).No
Request samplePOST /v1/whatsapp/templates
{
  "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
}
Response sample200 OK
{
  "name": "morisbox_invite",
  "language": "fr",
  "status": "PENDING",
  "provider_template_id": "…",
  "message": "Submitted to 360dialog/Meta for approval"
}
GET/v1/whatsapp/templates/{name}

Template detail and latest status (PENDING / APPROVED / REJECTED) plus rejection_reason when available.

AuthX-API-Key (whatsapp:send)
Parameters
NameTypeDescriptionRequired
namepathTemplate name.Yes
languagequeryLanguage code (default fr).No
Request sampleGET /v1/whatsapp/templates/{name}
No request body.
Response sample200 OK
{
  "name": "morisbox_invite",
  "language": "fr",
  "category": "UTILITY",
  "status": "APPROVED",
  "has_url_button": true,
  "body_param_count": 1
}
POST/v1/whatsapp/templates/{name}/sync

Re-sync template status from 360dialog/Meta into the local store.

AuthX-API-Key (whatsapp:send)
Parameters
NameTypeDescriptionRequired
namepathTemplate name.Yes
languagequeryLanguage code (default fr).No
Request samplePOST /v1/whatsapp/templates/{name}/sync
No request body.
Response sample200 OK
{
  "name": "morisbox_invite",
  "language": "fr",
  "status": "APPROVED",
  "message": "Template is approved and ready for delivery."
}
GET/v1/whatsapp/sessions

List 24h WhatsApp consent sessions (pending, active, declined).

AuthPortal token or X-API-Key (account:read)
Request sampleGET /v1/whatsapp/sessions
No request body.
Response sample200 OK
[
  {
    "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"
  }
]
GET/v1/whatsapp/sessions/{session_id}

Fetch one WhatsApp consent session by id.

AuthPortal token or X-API-Key (account:read)
Parameters
NameTypeDescriptionRequired
session_idpathSession identifier returned by OTP send or session list.Yes
Request sampleGET /v1/whatsapp/sessions/{session_id}
No request body.
Response sample200 OK
{
  "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"
}
GET/v1/whatsapp/messages

List sent WhatsApp messages and charged credits.

AuthPortal token or X-API-Key (account:read)
Request sampleGET /v1/whatsapp/messages
No request body.
Response sample200 OK
[
  {
    "id": "wm_123",
    "channel": "whatsapp",
    "billing_mode": "session",
    "to": "22890000000",
    "country": "TG",
    "status": "submitted",
    "charged_credits": 0,
    "session_id": "ws_123"
  }
]

WhatsApp inbound

GET/v1/inbound-notifications

List 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.

AuthPortal token or X-API-Key (whatsapp:receive or account:read)
Parameters
NameTypeDescriptionRequired
providerqueryOptional filter: ngh for SMS or whatsapp for WhatsApp (provider=seed returns all). A whatsapp:receive-only key must filter with provider=whatsapp.No
Request sampleGET /v1/inbound-notifications
No request body.
Response sample200 OK
[
  {
    "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"
  }
]
GET/v1/inbound-notifications/{notification_id}

Fetch one inbound WhatsApp or SMS message with the raw provider payload.

AuthPortal token or X-API-Key (whatsapp:receive or account:read)
Parameters
NameTypeDescriptionRequired
notification_idpathInbound notification id.Yes
Request sampleGET /v1/inbound-notifications/{notification_id}
No request body.
Response sample200 OK
{
  "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"
}