Full API Reference (Scalar)
openapi.json openapi.yaml Postman Collection
🧪 Sandbox Mode (Zero-cost test)Active API Key:
env_test_demo_2026
Interactive console and Swagger UI calls execute live against https://api.envoisms.ma.

Start here

Everything to get started before your first request.

EnvoiSMS.ma is a programmable messaging infrastructure built for Morocco. Our local routes to IAM, Inwi, and Orange, with automatic failover between routes, keep latency low.

Scalar • Business Grade
Full Interactive API Documentation (Scalar)

Test live endpoints, explore full OpenAPI 3.1 payload schemas, and generate cURL, PHP, Node.js, Python, and Go snippets instantly.

Open Full Scalar Docs
Base URLhttps://api.envoisms.ma
API Version/v1 (Stable)
Data Formatapplication/json; charset=utf-8
AuthenticationAuthorization: Bearer smr_xxx
Default Rate Limit60 to 1,200 requests / minute per key, by plan
Number FormatE.164 (e.g. +212612345678)
Active ChannelsSMS to Moroccan carriers (local routes), WhatsApp Business API (AtlasAI™)
Proprietary AI EngineAtlasAI™ — the engine behind Ghita, your WhatsApp assistant (Conversational Engine, Voice, Vision — WABA integrated)
LocationHosted in Morocco — EnvoiSMS Cloud Edge 🇲🇦
01

Generate your "smr_..." API key from your EnvoiSMS.ma Console.

02

Use Bearer authentication in your HTTP headers for every request.

03

Test your integration with Sandbox keys (env_test_...) on the live URL to validate your calls without spending real credit.

04

Integrate the WhatsApp Business API to divide your OTP costs by 10.

05

Configure a signed Webhook to receive delivery reports in real time.

06

Track your consumption and MAD invoices directly on your Dashboard.

Send your first message
// config/services.php
'envoisms' => [
    'key' => env('ENVOISMS_API_KEY'),
],

// Usage
Http::withToken(config('services.envoisms.key'))
    ->post('https://api.envoisms.ma/v1/messages', [
        'to' => '+212612345678',
        'message' => 'Votre commande est en cours de livraison 🚚',
        'from' => 'MaBoutique'
    ]);
OpenAPI Spec

Authentication

Bearer keys, headers, and security constraints.

Each /v1 endpoint requires a valid API key passed in the Authorization header as a Bearer token. Test and production keys can be generated or revoked from your console.

Authorization

Authorization: Bearer smr_xxx

Rate Limit Headers

X-RateLimit-Limit and X-RateLimit-Remaining returned with each call.

IP Allowlists

Requests coming from unconfigured IP addresses return a 401 status.

CORS Supported

JSON, Authorization, X-EnvoiSMS.ma-Signature, and X-EnvoiSMS.ma-Version are supported.

HTTP Headers Example
POST /v1/messages HTTP/1.1
Host: api.envoisms.ma
Authorization: Bearer smr_xxxxxxxx
Content-Type: application/json
Accept: application/json

Open Source

Official SDKs & Client Libraries

Integrate EnvoiSMS API into your application with our official open-source client libraries.

NPM PackageTypeScript & JavaScript
Node.js / TypeScript
npm install envoisms
GitHub Repository
Composer PackagePHP 8.0+
PHP Client
composer require envoisms/envoisms-php
GitHub Repository
PyPI PackagePython 3.8+
Python Client
pip install envoisms
GitHub Repository
Notification ChannelLaravel 9 - 11
Laravel OTP Package
composer require envoisms/laravel-otp
GitHub Repository
Go ModuleGo 1.20+
Go Client
go get github.com/envoisms/envoisms-go
GitHub Repository
Model Context ProtocolClaude, Cursor, AI Agents
MCP Server
npx -y @envoisms/mcp-server
GitHub Repository
DocsMessages/messages
POSThttps://api.envoisms.ma/v1/messages
Bearer Auth

Send a message

Sends an SMS or a WhatsApp Business message to a single recipient.

Request Body
FieldTypeRequirementDescription
tostringRequiredPhone number in E.164 format (+212...).
messagestringRequiredText content (max 1600 characters). Also accepted under the name "body".
fromstringOptionalCustom Sender ID (e.g. BRAND_NAME). Defaults to "EnvoiSMS".
channelstringOptional"sms" or "whatsapp". Defaults to "sms". "whatsapp" sends from your own connected WhatsApp Business number (403 WHATSAPP_NOT_CONNECTED otherwise). To send an ordinary text to a customer who uses WhatsApp, "sms" is enough: no connection, no template, no 24-hour window. Unless it is a template, a WhatsApp message is only accepted if the contact wrote to you in the last 24 hours — otherwise 400 OUT_OF_24H_WINDOW, with nothing charged (with cascade, WhatsApp is skipped for the next channel).
cascadebooleanOptionalIf enabled, tries WhatsApp, then falls back to SMS: at once if WhatsApp refuses the message or reports it failed (not on WhatsApp, 24-hour window closed, …), or when no delivery report arrives within cascade_timeout (120 s by default). Only a send that actually went out is billed: a refused or failed WhatsApp message is not, and you pay for the SMS alone. A WhatsApp message still waiting for its delivery report did go out (it can still be delivered when the phone reconnects), so it is billed along with its SMS fallback, and refunded if WhatsApp later reports it failed. Tip: give your WhatsApp template a time-to-live (TTL) no longer than cascade_timeout, so a phone that comes back online does not receive both.
cascade_timeoutintegerOptionalWith cascade: seconds to wait for a delivery report before the next channel, from 30 to 43200 (12 h). Default: 120. Shorter falls back faster but bills more double sends; longer means fewer duplicates.
metadataobjectOptionalCustom key-value pairs stored with the message and forwarded in webhooks (they are not returned by the GET /v1/messages endpoints). One key is meaningful to the platform: purpose: "otp" flags a one-time code you generate yourself and enables automatic redelivery (see notes).
buttonsarrayOptionalArray of interactive WhatsApp button objects (max 3, type "copy" | "url" | "call").
interactiveobjectOptionalRich WhatsApp interactive message object: dropdown list menus ("list"), quick reply buttons ("button"), native chat forms ("flow"), a link button ("cta_url": action {"name":"cta_url","parameters":{"display_text","url"}}) or a location request ("location_request_message").
templateobjectOptional(WhatsApp) An approved template of your account: {"name", "language"}, values in metadata.variables. The only message type allowed outside the 24-hour window; priced by the template's category.
image | video | audio | document | stickerobjectOptional(WhatsApp) Media by "id" (an uploaded media id) OR by "link" (an https URL), never both. "caption" on image, video and document; "filename" on document.
reactionobjectOptional(WhatsApp) {"message_id": wamid, "emoji": "👍"} — reacts to a message of the conversation; an empty emoji removes the reaction. Not billed.
contactsarrayOptional(WhatsApp) Contact cards (max 20): name.formatted_name required; phones, emails, urls, addresses, org, birthday optional.
locationobjectOptional(WhatsApp) Location pin: {"latitude", "longitude", "name"?, "address"?}.
contextobjectOptional(WhatsApp) {"message_id": wamid} — replies quoting an earlier message. Valid on every type except reaction.
phone_number_idstringOptional(WhatsApp) Which connected number sends; defaults to your default number.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
Notes d'implémentation
  • Sending OTP codes? Two approaches. /v1/verify/send handles the whole cycle (generation, delivery, validation, expiry) and remains the recommended path. If you generate your own codes and send them here, add metadata: {"purpose": "otp"}: on a network-confirmed delivery failure to a Moroccan number while the code is still fresh (under 10 minutes), the platform automatically resends it once over an alternative SMS route — same message id, no extra cost. Without this tag, the message is handled as an ordinary SMS.
  • WhatsApp: unless it is a template, a message (text, media, reaction, contacts, location, interactive) only goes to a contact who wrote to your number in the last 24 hours; otherwise the request is refused with 400 OUT_OF_24H_WINDOW before any charge. With a sandbox key the send is still simulated and the response reports the refusal under "warnings". To show "typing…" while you prepare the reply, see POST /v1/messages/typing.
  • Line breaks (\n) are fully supported on all channels and display correctly on the recipient side.
  • Rich text formatting (bold, italics, etc.) is not supported on the SMS channel (plain text only).
  • The WhatsApp channel supports text formatting with the standard syntax (*bold*, _italics_, ~strikethrough~).
POSThttps://api.envoisms.ma/v1/messages
curl -X POST "https://api.envoisms.ma/v1/messages" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+212612345678",
    "message": "Votre code de validation est 849204",
    "from": "MaBoutique",
    "channel": "whatsapp"
  }'
{
  "id": "msg_8f2d...",
  "to": "+212612345678",
  "channel": "whatsapp",
  "cascade": false,
  "status": "queued",
  "cost": {
    "eur": 0.03,
    "mad": 0.33
  },
  "segments": 1,
  "created_at": "2026-05-15T10:30:00Z"
}
DocsMessages/messages/bulk
POSThttps://api.envoisms.ma/v1/messages/bulk
Bearer Auth

Bulk send

Sends up to 10,000 messages in a single API call, each with its own recipient or content.

Request Body
FieldTypeRequirementDescription
messagesarrayRequiredArray of objects containing "to", "message" (or "body"), and an optional "metadata" object.
fromstringOptionalGlobal Sender ID for the whole batch.
channelstringOptionalGlobal channel ("sms" or "whatsapp"). Defaults to "sms".
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
Notes d'implémentation
  • Line breaks (\n) are fully supported in bulk message bodies.
  • Rich text formatting (bold, italics, etc.) is not supported on the SMS channel (plain text only).
  • The WhatsApp channel supports text formatting with the standard syntax (*bold*, _italics_, ~strikethrough~).
POSThttps://api.envoisms.ma/v1/messages/bulk
curl -X POST "https://api.envoisms.ma/v1/messages/bulk" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "to": "+212611111111",
        "message": "Hello Client 1"
      },
      {
        "to": "+212622222222",
        "message": "Hello Client 2"
      }
    ],
    "from": "ENVOISMS",
    "channel": "sms"
  }'
{
  "batch_id": "batch_9a3c...",
  "total": 2,
  "channel": "sms",
  "estimated_cost": {
    "eur": 0.056,
    "mad": 0.62
  },
  "messages": [
    {
      "id": "msg_1a2b...",
      "to": "+212611111111",
      "status": "queued"
    },
    {
      "id": "msg_3c4d...",
      "to": "+212622222222",
      "status": "queued"
    }
  ]
}
DocsMessages/messages/typing
POSThttps://api.envoisms.ma/v1/messages/typing
Bearer Auth

WhatsApp typing indicator

Marks a received WhatsApp message as read and shows its sender "typing…" while you prepare the reply (clears when you reply, or after about 25 seconds). Not a message: nothing is stored or billed.

Request Body
FieldTypeRequirementDescription
message_idstringRequiredWhatsApp id (wamid) of the received message you are answering.
typing_indicatorbooleanOptionalfalse sends the read receipt only. Default: true.
phone_number_idstringOptionalConnected number that received the message; defaults to your default number.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
POSThttps://api.envoisms.ma/v1/messages/typing
curl -X POST "https://api.envoisms.ma/v1/messages/typing" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{}'
{
  "status": "ok",
  "message_id": "wamid.HBgLMjEyNjEyMzQ1Njc4FQIAEhgg",
  "typing_indicator": true
}
DocsMessages/messages
GEThttps://api.envoisms.ma/v1/messages
Bearer Auth

List messages

Retrieves a paginated list of all messages sent from the account.

Query Parameters
ParameterTypeRequirementDescription
limitintegerOptionalNumber of results to return (1-200, default: 50).
offsetintegerOptionalNumber of results to skip for pagination (default: 0).
statusstringOptionalFilter by status (queued, sent, delivered, failed, undeliverable, unconfirmed).
channelstringOptionalFilter by channel (sms, whatsapp).
from_datestringOptionalFilter by start date (ISO 8601 format).
to_datestringOptionalFilter by end date (ISO 8601 format).
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
GEThttps://api.envoisms.ma/v1/messages
curl -X GET "https://api.envoisms.ma/v1/messages?limit=50&offset=0" \
  -H "Authorization: Bearer smr_xxx"
{
  "data": [
    {
      "id": "msg_8f2d...",
      "to": "+212612345678",
      "channel": "whatsapp",
      "body": "Votre code de validation est 849204",
      "sender_id": "MaBanque",
      "status": "delivered",
      "cost_mad": 0.13,
      "created_at": "2026-05-15T10:30:00Z"
    }
  ],
  "limit": 50,
  "offset": 0,
  "total": 1
}
DocsMessages/messages/:id
GEThttps://api.envoisms.ma/v1/messages/:id
Bearer Auth

Message status

Retrieves the details and real-time delivery status of a specific message.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
Notes d'implémentation
  • Possible statuses: queued, sent, delivered, failed, undeliverable, unconfirmed. "unconfirmed" means the operator never confirmed nor denied delivery — a receipt arriving later can still replace it.
  • The metadata field provided at send time is not returned here; it is forwarded in webhooks.
GEThttps://api.envoisms.ma/v1/messages/:id
curl -X GET "https://api.envoisms.ma/v1/messages/:id" \
  -H "Authorization: Bearer smr_xxx"
{
  "id": "msg_8f2d...",
  "campaign_id": null,
  "to": "+212612345678",
  "channel": "sms",
  "body": "Votre code de validation est 849204",
  "sender_id": "MaBanque",
  "unicode": 0,
  "segments": 1,
  "status": "delivered",
  "error_code": null,
  "error_message": null,
  "cost_eur": 0.0436,
  "cost_mad": 0.48,
  "scheduled_at": null,
  "sent_at": "2026-05-15T10:30:02Z",
  "delivered_at": "2026-05-15T10:30:05Z",
  "failed_at": null,
  "operator": "Maroc Telecom",
  "sandbox": 0,
  "created_at": "2026-05-15T10:30:00Z"
}
DocsMessages/templates
GEThttps://api.envoisms.ma/v1/templates
Bearer Auth

List templates

Retrieves all approved WhatsApp and SMS templates on your account.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
GEThttps://api.envoisms.ma/v1/templates
curl -X GET "https://api.envoisms.ma/v1/templates" \
  -H "Authorization: Bearer smr_xxx"
{
  "data": [
    {
      "id": "tpl_1234...",
      "name": "otp_verification",
      "channel": "whatsapp",
      "body": "Votre code de validation est {{1}}",
      "status": "approved",
      "category": "otp",
      "created_at": "2026-05-15T10:30:00Z"
    }
  ],
  "total": 1
}
DocsMessages/templates
POSThttps://api.envoisms.ma/v1/templates
Bearer Auth

Create a template

Submits a new template for approval by the carriers or WhatsApp.

Request Body
FieldTypeRequirementDescription
namestringRequiredInternal name of the template.
channelstringRequired"sms" or "whatsapp".
bodystringRequiredMessage content with variables (e.g. {{1}}).
categorystringOptionalTemplate category (e.g. "otp", "marketing").
languagestringOptionalWhatsApp: Meta language code (e.g. "fr", "ar", "en_US"). Defaults to "fr".
sample_valuesobject | string[]OptionalWhatsApp: one example value per variable, required for Meta review (e.g. {"1": "Amine"} or ["Amine"]).
header_example_urlstringOptionalWhatsApp, image/video/document header: public https link to an example file, uploaded to Meta for review.
add_security_recommendationbooleanOptionalWhatsApp, "authentication" category: adds Meta's security notice. The body text is fixed by Meta; code_expiration_minutes (1-90) is also accepted.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
Notes d'implémentation
  • A WhatsApp template first goes through EnvoiSMS review (pending_admin), then is submitted to Meta on your own WhatsApp Business Account (pending_meta). Only Meta approves it (approved); its final category is the one Meta assigns.
POSThttps://api.envoisms.ma/v1/templates
curl -X POST "https://api.envoisms.ma/v1/templates" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{}'
{
  "id": "tpl_1234...",
  "name": "order_confirmation",
  "channel": "whatsapp",
  "language": "fr",
  "status": "pending_admin"
}
DocsMessages/templates/sync
POSThttps://api.envoisms.ma/v1/templates/sync
Bearer Auth

Sync templates from Meta

Imports every template of your WhatsApp Business Account (status, category, quality). A template Meta no longer holds becomes deleted_on_meta.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
POSThttps://api.envoisms.ma/v1/templates/sync
curl -X POST "https://api.envoisms.ma/v1/templates/sync" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{}'
{
  "ok": true,
  "wabas": ["1234567890"],
  "fetched": 12,
  "updated": 9,
  "inserted": 3,
  "marked_deleted": 1,
  "complete": true,
  "errors": []
}
DocsMessages/sender-ids
GEThttps://api.envoisms.ma/v1/sender-ids
Bearer Auth

List Sender IDs

Retrieves the list of your Sender IDs with their approval status.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
GEThttps://api.envoisms.ma/v1/sender-ids
curl -X GET "https://api.envoisms.ma/v1/sender-ids" \
  -H "Authorization: Bearer smr_xxx"
{
  "data": [
    {
      "id": "sid_9a8b...",
      "sender_id": "MABANQUE",
      "status": "approved",
      "requested_at": "2026-05-10T09:00:00Z"
    }
  ]
}
DocsMessages/sender-ids
POSThttps://api.envoisms.ma/v1/sender-ids
Bearer Auth

Request a Sender ID

Submits a new Sender ID for approval (required for Morocco).

Request Body
FieldTypeRequirementDescription
sender_idstringRequiredThe desired sender name (max 11 characters).
rc_urlstringOptionalLink to the Trade Register (Registre de Commerce) for verification.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
POSThttps://api.envoisms.ma/v1/sender-ids
curl -X POST "https://api.envoisms.ma/v1/sender-ids" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{}'
{
  "id": "sid_9a8b...",
  "sender_id": "MABANQUE",
  "status": "pending"
}
DocsWhatsApp/whatsapp/profile
GEThttps://api.envoisms.ma/v1/whatsapp/profile
Bearer Auth

WhatsApp Business profile

Retrieves public information of your verified WhatsApp Business profile (name, photo, description, address, website).

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
GEThttps://api.envoisms.ma/v1/whatsapp/profile
curl -X GET "https://api.envoisms.ma/v1/whatsapp/profile" \
  -H "Authorization: Bearer smr_xxx"
{
  "about": "Service client officiel EnvoiSMS.ma",
  "address": "Casablanca, Maroc",
  "description": "Infrastructure de messagerie programmable pour les entreprises au Maroc.",
  "email": "[email protected]",
  "websites": [
    "https://votremarque.ma"
  ],
  "profile_picture_url": "https://pps.whatsapp.net/v/..."
}
DocsWhatsApp/whatsapp/profile
POSThttps://api.envoisms.ma/v1/whatsapp/profile
Bearer Auth

Update WhatsApp profile

Updates the customer-facing information on your official WhatsApp Business profile.

Request Body
FieldTypeRequirementDescription
aboutstringOptionalShort text status (max 139 characters).
descriptionstringOptionalDetailed business description (max 512 characters).
addressstringOptionalPhysical address of your office or store.
emailstringOptionalCustomer support contact email address.
websitesarrayOptionalArray containing up to 2 website URLs.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
POSThttps://api.envoisms.ma/v1/whatsapp/profile
curl -X POST "https://api.envoisms.ma/v1/whatsapp/profile" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{}'
{
  "success": true,
  "updated_at": "2026-08-30T10:00:00Z"
}
DocsVerify/verify/send
POSThttps://api.envoisms.ma/v1/verify/send
Bearer Auth

Generate an OTP

Sends a one-time verification code. Two modes, your choice: "whatsapp" — managed verification, the simplest (EnvoiSMS generates the code and delivers it on WhatsApp from a verified sender; if Meta refuses the WhatsApp send, the code goes out by SMS; with a Verify app configured for auto_cascade, the SMS also goes out once the channel timeout expires, 30 s by default; no code to store on your side) — or "sms" — you keep full control of the code, the template and the sender. In both cases, validation is done with the same /v1/verify/check call. Want your own brand on the managed verification's SMS fallback? Available on request: [email protected].

Request Body
FieldTypeRequirementDescription
tostringRequiredRecipient in E.164 format.
channelstringOptional"whatsapp" (managed verification: WhatsApp, SMS fallback if Meta refuses the send or, with auto_cascade, after the channel timeout; code valid 10 min, 3 attempts, billed per verification at the WhatsApp OTP rate) or "sms" (code generated for you, sent with your brand). Default: "sms".
app_idstringOptionalID of the Verify application configured on the dashboard (e.g. vra_...). Automatically applies the code settings, timings and channel cascade.
brandstringOptional(sms channel) Displayed brand name (e.g. MonApp, max 32 chars). Defaults to "EnvoiSMS".
code_lengthintegerOptional(sms channel) Length of the generated code (4 to 8 digits, default: 6).
expiryintegerOptionalCode validity duration in seconds (60 to 1800, default: 600). On the whatsapp channel it is capped at 600 (the authentication template's limit).
cascadearrayOptional(sms channel) Ordered list of channels for automatic cascading fallback (e.g. ["whatsapp", "sms"]).
templatestringOptional(sms channel) Custom text with the {{code}} and {{brand}} variables. In whatsapp mode, the localized message (fr/en/es) is managed for you.
otp_button_textstringOptional(whatsapp channel) Custom label for the WhatsApp auto-copy button (max 25 chars).
web_otp_domainstringOptional(Optional, sms channel) Target web domain for W3C WebOTP autofill (e.g. "https://mysite.ma"). Appends @domain #code to the SMS.
app_hashstringOptional(Optional, sms channel) 11-character Android app signature hash (SMS Retriever API) for automatic OTP detection on Android.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
Notes d'implémentation
  • OTP Autofill (iOS & Android): To allow your users to automatically 1-tap fill the received code above their keyboard, simply add autocomplete="one-time-code" and inputmode="numeric" to the <input> field on your website.
  • Anti-spam cooldown: The "resend_after_seconds" field indicates the exact cooldown before retrying (60s for WhatsApp according to Meta standards, 30s for SMS). A daily cap of 10 verifications per destination applies.
POSThttps://api.envoisms.ma/v1/verify/send
curl -X POST "https://api.envoisms.ma/v1/verify/send" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+212612345678",
    "brand": "MonApp",
    "channel": "whatsapp",
    "code_length": 6,
    "expiry": 600
  }'
{
  "session_id": "vrf_7e2a...",
  "to": "+212612345678",
  "channel": "whatsapp",
  "resend_after_seconds": 60,
  "fallback_channels": ["sms", "whatsapp"],
  "expires_at": "2026-05-15T10:35:00Z",
  "status": "sent",
  "cost": { "eur": 0.05, "mad": 0.55 }
}
DocsVerify/verify/resend
POSThttps://api.envoisms.ma/v1/verify/resend
Bearer Auth

Resend OTP Code (SMS or WhatsApp)

Resends the same active OTP code over SMS or WhatsApp after the cooldown (30s for SMS, 60s for WhatsApp). No new code is generated, preventing user input conflicts.

Request Body
FieldTypeRequirementDescription
session_idstringRequiredSession ID received from the initial /v1/verify/send call.
channelstringOptionalTarget resend channel ("sms" | "whatsapp"). Default: "sms".
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
Notes d'implémentation
  • Available on every plan, Starter included: /v1/verify/resend requires the same "verify" permission as /v1/verify/send, nothing more.
  • Where the countdown starts: the cooldown runs from the last actual delivery, not from your call — 60s after a WhatsApp delivery, 30s after an SMS one. GET /v1/verify/{session_id} returns the live countdown ("resend_available_in_seconds") and the 429's Retry-After header carries the exact seconds remaining. The window is scoped to your account and the destination: another customer's traffic to the same number never blocks you.
  • Sandbox keys (env_test_): nothing is delivered and nothing is charged. The response then carries "sandbox": true and "sandbox_code", so a simulated resend is never mistaken for a delivered one. A sandbox key can only resend sessions it created, and a live key only live sessions.
POSThttps://api.envoisms.ma/v1/verify/resend
curl -X POST "https://api.envoisms.ma/v1/verify/resend" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{}'
{
  "session_id": "vrf_7e2a...",
  "message_id": "wamid.HBgMMjEy...",
  "to": "+212612345678",
  "channel": "whatsapp",
  "status": "sent",
  "resend_after_seconds": 60,
  "expires_at": "2026-05-15T10:35:00Z"
}
DocsVerify/verify/check
POSThttps://api.envoisms.ma/v1/verify/check
Bearer Auth

Verify an OTP

Validates the code provided by the user for a given verification session.

Request Body
FieldTypeRequirementDescription
session_idstringRequiredSession ID received from the /v1/verify/send call.
codestringRequiredThe code received and entered by the user.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
POSThttps://api.envoisms.ma/v1/verify/check
curl -X POST "https://api.envoisms.ma/v1/verify/check" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "vrf_7e2a...",
    "code": "849204"
  }'
{
  "session_id": "vrf_7e2a...",
  "verified": true,
  "verified_at": "2026-05-15T10:35:12Z"
}
DocsVerify/verify/:id
GEThttps://api.envoisms.ma/v1/verify/:id
Bearer Auth

OTP session status

Retrieves the state (validated or expired) of a specific verification session.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
GEThttps://api.envoisms.ma/v1/verify/:id
curl -X GET "https://api.envoisms.ma/v1/verify/:id" \
  -H "Authorization: Bearer smr_xxx"
{
  "id": "vrf_7e2a...",
  "to": "+212612345678",
  "channel": "whatsapp",
  "expires_at": "2026-05-15T10:40:00Z",
  "verified_at": "2026-05-15T10:35:12Z",
  "created_at": "2026-05-15T10:30:00Z"
}
DocsVerify/verify/lookup
POSThttps://api.envoisms.ma/v1/verify/lookup
Bearer Auth

Number lookup

Validates the format, carrier, line type and geographic location of a phone number.

Request Body
FieldTypeRequirementDescription
numberstringRequiredThe phone number to validate (local or international format).
country_codestringOptional2-letter ISO country code (e.g. MA, FR). Recommended when the number is in local format.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
POSThttps://api.envoisms.ma/v1/verify/lookup
curl -X POST "https://api.envoisms.ma/v1/verify/lookup" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "number": "+212612345678",
    "country_code": "MA"
  }'
{
  "valid": true,
  "number": "212612345678",
  "local_format": "0612345678",
  "international_format": "+212612345678",
  "country_prefix": "212",
  "country_code": "MA",
  "country_name": "Morocco",
  "location": "Casablanca",
  "carrier": "Maroc Telecom (IAM)",
  "line_type": "mobile"
}
DocsConversations/conversations
GEThttps://api.envoisms.ma/v1/conversations
Bearer Auth

List conversations

Retrieves active WhatsApp conversation threads with real-time 24-hour service window tracking and bot mute status.

Query Parameters
ParameterTypeRequirementDescription
limitintegerOptionalMaximum number of conversations to return (default 30, max 100).
bot_statusstringOptionalFilter by bot status ("active" or "muted").
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
GEThttps://api.envoisms.ma/v1/conversations
curl -X GET "https://api.envoisms.ma/v1/conversations?limit=50&offset=0" \
  -H "Authorization: Bearer smr_xxx"
{
  "conversations": [
    {
      "phone": "+212612345678",
      "contact_name": "Yassine Alami",
      "last_message": "Bonjour, je souhaite visiter l'appartement témoin",
      "last_message_at": "2026-08-30T11:42:00Z",
      "unread_count": 1,
      "bot_muted": true,
      "can_reply_free": true,
      "window_expires_at": "2026-08-31T11:42:00Z"
    }
  ],
  "total": 1
}
DocsConversations/conversations/:phone
GEThttps://api.envoisms.ma/v1/conversations/:phone
Bearer Auth

Conversation history

Retrieves the complete message thread exchanged with a specific WhatsApp contact.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
GEThttps://api.envoisms.ma/v1/conversations/:phone
curl -X GET "https://api.envoisms.ma/v1/conversations/:phone" \
  -H "Authorization: Bearer smr_xxx"
{
  "phone": "+212612345678",
  "contact_name": "Yassine Alami",
  "bot_muted": true,
  "messages": [
    {
      "id": "msg_01J...",
      "direction": "inbound",
      "body": "Je souhaite des informations sur le projet Casablanca Marina",
      "type": "text",
      "timestamp": "2026-08-30T11:40:00Z"
    },
    {
      "id": "msg_02J...",
      "direction": "outbound",
      "body": "Bonjour Yassine ! Quel type de bien recherchez-vous ?",
      "type": "interactive",
      "timestamp": "2026-08-30T11:40:05Z"
    }
  ]
}
DocsConversations/conversations/:phone/messages
POSThttps://api.envoisms.ma/v1/conversations/:phone/messages
Bearer Auth

Send live chat reply

Sends an agent reply and mutes the bot on that conversation (re-enable with POST /v1/conversations/:phone/toggle-bot). A free-form WhatsApp message requires the contact to have written in the last 24 hours (otherwise 400 OUT_OF_24H_WINDOW); an approved template can be sent at any time.

Request Body
FieldTypeRequirementDescription
messagestringOptionalText of the reply (or caption of the attached media). Required without a template or media.
channelstringOptional"whatsapp" (default) or "sms".
template_namestringOptionalApproved WhatsApp template to send instead of free text (with template_language and variables).
media_urlstringOptionalhttps URL (or data URI) of an image or PDF to attach; media_type "image" or "document".
phone_number_idstringOptionalConnected WhatsApp number that replies.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
POSThttps://api.envoisms.ma/v1/conversations/:phone/messages
curl -X POST "https://api.envoisms.ma/v1/conversations/:phone/messages" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{}'
{
  "ok": true,
  "message": {
    "id": "msg_03J...",
    "phone": "212612345678",
    "role": "agent",
    "message": "Bonjour Yassine, votre visite est confirmée.",
    "status": "sent",
    "channel": "whatsapp",
    "created_at": "2026-08-30T11:45:00Z"
  }
}
DocsLeads/qualified-leads
GEThttps://api.envoisms.ma/v1/qualified-leads
Bearer Auth

List qualified leads

Retrieves leads automatically qualified by AtlasAI™ with their BANT score and CRM pipeline stage.

Query Parameters
ParameterTypeRequirementDescription
stagestringOptionalFilter by pipeline stage ("new", "contacted", "meeting_scheduled", "closed_won", "closed_lost").
min_scoreintegerOptionalMinimum BANT score (0 to 100).
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
Notes d'implémentation
  • Leads are scored by AtlasAI™ Conversational Engine, AtlasAI™ Voice, and AtlasAI™ Vision directly from inbound WhatsApp messages. These AI modules are integrated natively within WABA and are not available as standalone external APIs.
GEThttps://api.envoisms.ma/v1/qualified-leads
curl -X GET "https://api.envoisms.ma/v1/qualified-leads?limit=50&offset=0" \
  -H "Authorization: Bearer smr_xxx"
{
  "leads": [
    {
      "id": "lead_01J...",
      "phone": "+212612345678",
      "name": "Dr. Benjelloun",
      "preset": "medical_equipment",
      "bant_score": 85,
      "is_hot": true,
      "breakdown": {
        "budget": 25,
        "authority": 25,
        "need": 20,
        "timeline": 15
      },
      "intent": "Échographe Doppler pour nouveau cabinet",
      "stage": "meeting_scheduled",
      "created_at": "2026-08-30T09:15:00Z"
    }
  ],
  "total": 1
}
DocsLeads/qualified-leads/:id/status
POSThttps://api.envoisms.ma/v1/qualified-leads/:id/status
Bearer Auth

Update lead status

Updates the sales pipeline stage for a qualified lead.

Request Body
FieldTypeRequirementDescription
statusstringRequired"new" | "contacted" | "meeting_scheduled" | "closed_won" | "closed_lost"
notesstringOptionalInternal sales follow-up notes.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
POSThttps://api.envoisms.ma/v1/qualified-leads/:id/status
curl -X POST "https://api.envoisms.ma/v1/qualified-leads/:id/status" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{}'
{
  "success": true,
  "lead_id": "lead_01J...",
  "stage": "meeting_scheduled"
}
DocsContacts/contacts
GEThttps://api.envoisms.ma/v1/contacts
Bearer Auth

List contacts

Retrieves all contacts on your account, with keyword or list filtering.

Query Parameters
ParameterTypeRequirementDescription
limitintegerOptionalResults per page (1-500, default: 100).
offsetintegerOptionalPagination offset (default: 0).
qstringOptionalSearch by name, phone or email.
list_idstringOptionalFilter to members of a specific contact list only.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
GEThttps://api.envoisms.ma/v1/contacts
curl -X GET "https://api.envoisms.ma/v1/contacts?limit=50&offset=0" \
  -H "Authorization: Bearer smr_xxx"
{
  "data": [
    {
      "id": "ctc_a2b3...",
      "phone": "+212612345678",
      "name": "Karim Bennani",
      "email": "[email protected]",
      "custom1": "VIP",
      "custom2": null,
      "custom3": null,
      "created_at": "2026-05-10T14:20:00Z"
    }
  ],
  "limit": 100,
  "offset": 0,
  "total": 1
}
DocsContacts/contacts
POSThttps://api.envoisms.ma/v1/contacts
Bearer Auth

Create / Update a contact

Adds a contact or updates an existing contact's information (matched by number).

Request Body
FieldTypeRequirementDescription
phonestringRequiredPhone number in E.164 format.
namestringOptionalFull name of the contact.
emailstringOptionalEmail address.
list_idstringOptionalImmediately attach the contact to an existing list.
custom_fieldsobjectOptionalObject containing up to 3 custom fields ("custom1", "custom2", "custom3").
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
POSThttps://api.envoisms.ma/v1/contacts
curl -X POST "https://api.envoisms.ma/v1/contacts" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "+212612345678",
    "name": "Karim Bennani",
    "email": "[email protected]",
    "list_id": "lst_f84b..."
  }'
{
  "id": "ctc_a2b3...",
  "phone": "+212612345678",
  "name": "Karim Bennani",
  "email": "[email protected]",
  "custom1": "VIP",
  "custom2": null,
  "custom3": null,
  "created_at": "2026-05-10T14:20:00Z"
}
DocsContacts/contacts/import
POSThttps://api.envoisms.ma/v1/contacts/import
Bearer Auth

Import contacts

Bulk-imports up to 5,000 contacts in a single call.

Request Body
FieldTypeRequirementDescription
contactsarrayRequiredArray of objects containing "phone", "name" (optional) and "email" (optional).
list_idstringOptionalID of the list to import the group into.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
POSThttps://api.envoisms.ma/v1/contacts/import
curl -X POST "https://api.envoisms.ma/v1/contacts/import" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "contacts": [
      {
        "phone": "+212611111111",
        "name": "Karim"
      },
      {
        "phone": "+212622222222",
        "name": "Youssef"
      }
    ],
    "list_id": "lst_f84b..."
  }'
{
  "imported": 150,
  "skipped": 3
}
DocsContacts/contacts/:id
DELETEhttps://api.envoisms.ma/v1/contacts/:id
Bearer Auth

Delete a contact

Permanently deletes a contact by its unique identifier.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
DELETEhttps://api.envoisms.ma/v1/contacts/:id
curl -X DELETE "https://api.envoisms.ma/v1/contacts/:id" \
  -H "Authorization: Bearer smr_xxx"
{
  "deleted": true,
  "id": "ctc_a2b3..."
}
DocsContacts/contacts/lists
GEThttps://api.envoisms.ma/v1/contacts/lists
Bearer Auth

List contact lists

Retrieves all contact lists created for broadcast campaigns.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
GEThttps://api.envoisms.ma/v1/contacts/lists
curl -X GET "https://api.envoisms.ma/v1/contacts/lists" \
  -H "Authorization: Bearer smr_xxx"
{
  "data": [
    {
      "id": "lst_f84b...",
      "name": "Newsletter Clients",
      "description": "Clients inscrits à notre lettre d'information",
      "count": 1420,
      "created_at": "2026-04-15T09:00:00Z"
    }
  ]
}
DocsContacts/contacts/lists
POSThttps://api.envoisms.ma/v1/contacts/lists
Bearer Auth

Create a list

Creates a new empty group (contact list) intended for campaigns.

Request Body
FieldTypeRequirementDescription
namestringRequiredName of the list.
descriptionstringOptionalDescription of the list.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
POSThttps://api.envoisms.ma/v1/contacts/lists
curl -X POST "https://api.envoisms.ma/v1/contacts/lists" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Newsletter Clients",
    "description": "Clients inscrits"
  }'
{
  "id": "lst_f84b...",
  "name": "Newsletter Clients",
  "description": "Clients inscrits",
  "count": 0
}
DocsContacts/optouts
GEThttps://api.envoisms.ma/v1/optouts
Bearer Auth

List opt-outs

Retrieves the list of numbers that opted out (STOP) of your communications.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
GEThttps://api.envoisms.ma/v1/optouts
curl -X GET "https://api.envoisms.ma/v1/optouts" \
  -H "Authorization: Bearer smr_xxx"
{
  "data": [
    {
      "phone": "+212611111111",
      "opted_out_at": "2026-06-01T12:00:00Z"
    }
  ],
  "total": 1
}
DocsContacts/optouts
POSThttps://api.envoisms.ma/v1/optouts
Bearer Auth

Add an opt-out

Manually adds a number to your opt-out list (global blacklist).

Request Body
FieldTypeRequirementDescription
phonestringRequiredPhone number in E.164 format.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
POSThttps://api.envoisms.ma/v1/optouts
curl -X POST "https://api.envoisms.ma/v1/optouts" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{}'
{
  "success": true,
  "phone": "+212611111111",
  "opted_out_at": "2026-06-01T12:00:00Z"
}
DocsContacts/optouts/:phone
DELETEhttps://api.envoisms.ma/v1/optouts/:phone
Bearer Auth

Remove an opt-out

Removes a number from the opt-out list.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
DELETEhttps://api.envoisms.ma/v1/optouts/:phone
curl -X DELETE "https://api.envoisms.ma/v1/optouts/:phone" \
  -H "Authorization: Bearer smr_xxx"
{
  "deleted": true,
  "phone": "+212611111111"
}
DocsContacts/consents
GEThttps://api.envoisms.ma/v1/consents
Bearer Auth

List consent events

Append-only consent ledger (Law 09-08, art. 10): every grant and every withdrawal is a timestamped event with its source and evidence. The platform writes STOP/START keywords received on WhatsApp and SMS and the first customer-initiated conversation itself. Add format=csv to export the whole filtered set.

Query Parameters
ParameterTypeRequirementDescription
phonestringOptionalE.164 number to filter on.
purposestringOptionalmarketing, transactional, otp or service.
statusstringOptionalgranted or withdrawn.
fromstringOptionalEvents recorded at or after this ISO 8601 timestamp.
formatstringOptionalcsv to download the full set as a file.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
GEThttps://api.envoisms.ma/v1/consents
curl -X GET "https://api.envoisms.ma/v1/consents?limit=50&offset=0" \
  -H "Authorization: Bearer smr_xxx"
{
  "data": [
    {
      "id": "cns_9f2a...",
      "phone": "+212612345678",
      "channel": "whatsapp",
      "purpose": "marketing",
      "status": "withdrawn",
      "source": "whatsapp_keyword",
      "evidence": { "keyword": "stop", "lang": "fr" },
      "recorded_at": "2026-09-05T09:00:00.000Z",
      "created_at": "2026-09-05T09:00:00.000Z"
    }
  ],
  "total": 1,
  "limit": 100,
  "offset": 0
}
DocsContacts/consents
POSThttps://api.envoisms.ma/v1/consents
Bearer Auth

Record a consent event

Records that a person granted or withdrew consent, with the evidence you hold (wording shown, URL, IP, reference). recorded_at may be backdated for an imported consent, never set in the future. A marketing withdrawal is placed on the opt-out list immediately.

Request Body
FieldTypeRequirementDescription
phonestringRequiredPhone number in E.164 format.
purposestringRequiredmarketing, transactional, otp or service.
statusstringOptionalgranted (default) or withdrawn.
channelstringOptionalwhatsapp, sms or any (default).
sourcestringOptionalapi (default), form, import or dashboard. Keyword sources are reserved to the platform.
evidenceobjectOptionalFree-form JSON object (< 4 KB): wording shown, url, ip, reference.
recorded_atstringOptionalISO 8601 timestamp of the person's act (default: now).
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
POSThttps://api.envoisms.ma/v1/consents
curl -X POST "https://api.envoisms.ma/v1/consents" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{}'
{
  "id": "cns_9f2a...",
  "phone": "+212612345678",
  "channel": "any",
  "purpose": "marketing",
  "status": "granted",
  "source": "form",
  "evidence": { "text": "J'accepte de recevoir les offres par WhatsApp", "url": "https://example.ma/inscription" },
  "recorded_at": "2026-09-01T10:00:00.000Z",
  "created_at": "2026-09-09T12:00:00.000Z"
}
DocsContacts/consents/:phone
GEThttps://api.envoisms.ma/v1/consents/:phone
Bearer Auth

Consent state and history for a number

The answer to “show me this person's consent”: current state per purpose (derived from the newest event — unknown without history, never assumed granted), opt-out list membership, and the full history.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
GEThttps://api.envoisms.ma/v1/consents/:phone
curl -X GET "https://api.envoisms.ma/v1/consents/:phone" \
  -H "Authorization: Bearer smr_xxx"
{
  "phone": "+212612345678",
  "opted_out": true,
  "opted_out_at": "2026-09-05T09:00:00Z",
  "state": {
    "marketing": { "status": "withdrawn", "channel": "whatsapp", "source": "whatsapp_keyword", "recorded_at": "2026-09-05T09:00:00.000Z", "record_id": "cns_3" },
    "transactional": { "status": "unknown" },
    "otp": { "status": "unknown" },
    "service": { "status": "granted", "channel": "whatsapp", "source": "whatsapp_inbound", "recorded_at": "2026-07-01T09:00:00.000Z", "record_id": "cns_1" }
  },
  "history": []
}
DocsCampaigns/campaigns
GEThttps://api.envoisms.ma/v1/campaigns
Bearer Auth

List campaigns

Retrieves all your scheduled or currently running broadcast campaigns.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
GEThttps://api.envoisms.ma/v1/campaigns
curl -X GET "https://api.envoisms.ma/v1/campaigns" \
  -H "Authorization: Bearer smr_xxx"
{
  "data": [
    {
      "id": "cmp_8d2a...",
      "name": "Soldes d'été 2026",
      "channel": "sms",
      "list_id": "lst_f84b...",
      "template_id": null,
      "body": "Bonjour {{name}}, profitez de -50% sur toute la collection avec le code ETE50 !",
      "sender_id": "SOLDES",
      "status": "draft",
      "scheduled_at": null,
      "created_at": "2026-06-01T12:00:00Z"
    }
  ]
}
DocsCampaigns/campaigns
POSThttps://api.envoisms.ma/v1/campaigns
Bearer Auth

Create a campaign

Saves a new campaign as a draft or schedules it for a specific date.

Request Body
FieldTypeRequirementDescription
namestringRequiredName of the campaign.
bodystringRequiredMessage body. May use the {{name}} variable. (One of the two is required: body or template_id.)
template_idstringRequiredAlternatively, the ID of an approved template. (One of the two is required: body or template_id.)
channelstringOptional"sms" or "whatsapp". Defaults to "sms".
list_idstringOptionalID of the recipient contact list.
sender_idstringOptionalSender name.
scheduled_atstringOptionalScheduled date (ISO 8601). Sets the status to "scheduled".
buttonsarrayOptionalArray of interactive WhatsApp buttons (max 3, type "copy" | "url" | "call").
metadataobjectOptionalCustom metadata (e.g. throttling / send-rate options: {"throttling": "50_min"}).
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
POSThttps://api.envoisms.ma/v1/campaigns
curl -X POST "https://api.envoisms.ma/v1/campaigns" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Soldes d'été 2026",
    "body": "Bonjour {{name}}, profitez de -50% avec le code ETE50 !",
    "channel": "sms",
    "list_id": "lst_f84b...",
    "sender_id": "SOLDES"
  }'
{
  "id": "cmp_8d2a...",
  "status": "draft"
}
DocsCampaigns/campaigns/:id
GEThttps://api.envoisms.ma/v1/campaigns/:id
Bearer Auth

Campaign details

Retrieves the details, schedule and execution status of a campaign.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
GEThttps://api.envoisms.ma/v1/campaigns/:id
curl -X GET "https://api.envoisms.ma/v1/campaigns/:id" \
  -H "Authorization: Bearer smr_xxx"
{
  "id": "cmp_8d2a...",
  "name": "Soldes d'été 2026",
  "channel": "sms",
  "list_id": "lst_f84b...",
  "body": "Bonjour {{name}}, profitez de -50%...",
  "sender_id": "SOLDES",
  "status": "running",
  "total_count": 1420,
  "sent_count": 840,
  "started_at": "2026-06-15T10:00:00Z",
  "created_at": "2026-06-01T12:00:00Z"
}
DocsCampaigns/campaigns/:id/send
POSThttps://api.envoisms.ma/v1/campaigns/:id/send
Bearer Auth

Launch a campaign

Immediately starts broadcasting a draft campaign to all associated contacts.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
POSThttps://api.envoisms.ma/v1/campaigns/:id/send
curl -X POST "https://api.envoisms.ma/v1/campaigns/:id/send" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{}'
{
  "id": "cmp_8d2a...",
  "queued": 1420,
  "total": 1420
}
DocsWebhooks/webhooks
GEThttps://api.envoisms.ma/v1/webhooks
Bearer Auth

List webhooks

Retrieves the list of all your registered webhook endpoints.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
GEThttps://api.envoisms.ma/v1/webhooks
curl -X GET "https://api.envoisms.ma/v1/webhooks" \
  -H "Authorization: Bearer smr_xxx"
{
  "data": [
    {
      "id": "whk_5f2b...",
      "url": "https://mon-serveur.ma/api/envoisms-receiver",
      "events": [
        "message.delivered",
        "message.failed"
      ],
      "active": true,
      "last_triggered_at": "2026-06-15T09:30:15Z",
      "last_status": 200,
      "created_at": "2026-05-01T10:00:00Z"
    }
  ]
}
DocsWebhooks/webhooks
POSThttps://api.envoisms.ma/v1/webhooks
Bearer Auth

Create a Webhook

Registers an HTTPS callback URL to receive event notifications.

Request Body
FieldTypeRequirementDescription
urlstringRequiredSecure target URL starting with "https://".
eventsarrayOptionalArray of events (e.g. ["message.delivered", "message.failed"]). The wildcards "message.*" and "*" are accepted. Default: ["message.delivered", "message.failed"].
secretstringOptionalSecret signing key. If not provided, it will be generated automatically.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
POSThttps://api.envoisms.ma/v1/webhooks
curl -X POST "https://api.envoisms.ma/v1/webhooks" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://mon-serveur.ma/api/envoisms-receiver",
    "events": [
      "message.delivered",
      "message.failed"
    ]
  }'
{
  "id": "whk_5f2b...",
  "url": "https://mon-serveur.ma/api/envoisms-receiver",
  "events": [
    "message.delivered",
    "message.failed"
  ],
  "secret": "whsec_2f8a9e7d...",
  "active": true
}
DocsWebhooks/webhooks/:id
PATCHhttps://api.envoisms.ma/v1/webhooks/:id
Bearer Auth

Update a Webhook

Updates a webhook's configuration (URL, subscribed events or active state).

Request Body
FieldTypeRequirementDescription
urlstringOptionalNew HTTPS URL.
eventsarrayOptionalNew list of subscribed events.
activebooleanOptionalEnable (true) or disable (false) the webhook.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
PATCHhttps://api.envoisms.ma/v1/webhooks/:id
curl -X PATCH "https://api.envoisms.ma/v1/webhooks/:id" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://mon-serveur.ma/api/envoisms-receiver-updated",
    "active": false
  }'
{
  "updated": true,
  "id": "whk_5f2b..."
}
DocsWebhooks/webhooks/:id
DELETEhttps://api.envoisms.ma/v1/webhooks/:id
Bearer Auth

Delete a Webhook

Deactivates and soft-deletes a webhook endpoint.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
DELETEhttps://api.envoisms.ma/v1/webhooks/:id
curl -X DELETE "https://api.envoisms.ma/v1/webhooks/:id" \
  -H "Authorization: Bearer smr_xxx"
{
  "deleted": true,
  "id": "whk_5f2b..."
}
DocsWebhooks/webhooks/:id/test
POSThttps://api.envoisms.ma/v1/webhooks/:id/test
Bearer Auth

Test a Webhook

Triggers a test event ("message.test") to the webhook's configured URL.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
POSThttps://api.envoisms.ma/v1/webhooks/:id/test
curl -X POST "https://api.envoisms.ma/v1/webhooks/:id/test" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{}'
{
  "queued": true,
  "id": "whk_5f2b..."
}
DocsWebhooks/inbound-webhooks
GEThttps://api.envoisms.ma/v1/inbound-webhooks
Bearer Auth

List Inbound Webhooks

Retrieves all configured advertising lead capture endpoints on your account.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
GEThttps://api.envoisms.ma/v1/inbound-webhooks
curl -X GET "https://api.envoisms.ma/v1/inbound-webhooks" \
  -H "Authorization: Bearer smr_xxx"
{
  "webhooks": [
    {
      "id": "inw_9a8b...",
      "name": "Campagne TikTok Ads Casablanca",
      "source": "tiktok_ads",
      "catch_url": "https://api.envoisms.ma/v1/inbound-webhooks/catch/inw_9a8b...",
      "auto_start_funnel": true,
      "total_received": 142,
      "created_at": "2026-08-20T14:00:00Z"
    }
  ]
}
DocsWebhooks/inbound-webhooks
POSThttps://api.envoisms.ma/v1/inbound-webhooks
Bearer Auth

Create an Inbound Webhook

Generates a catch URL to receive instant leads from TikTok Lead Ads, Meta Lead Ads, Zapier, or Make.

Request Body
FieldTypeRequirementDescription
namestringRequiredDescriptive source name (e.g. "Facebook Lead Gen Summer Promo").
sourcestringOptionalSource identifier ("tiktok_ads", "meta_leads", "google_forms", "custom"). Defaults to "custom".
auto_start_funnelbooleanOptionalIf true, immediately initiates the AtlasAI™ WhatsApp qualification funnel upon lead receipt.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
POSThttps://api.envoisms.ma/v1/inbound-webhooks
curl -X POST "https://api.envoisms.ma/v1/inbound-webhooks" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{}'
{
  "id": "inw_9a8b...",
  "name": "Campagne TikTok Ads Casablanca",
  "catch_url": "https://api.envoisms.ma/v1/inbound-webhooks/catch/inw_9a8b...",
  "auto_start_funnel": true
}
DocsBilling/billing/balance
GEThttps://api.envoisms.ma/v1/billing/balance
Bearer Auth

Check balance

Retrieves the available balance in Moroccan Dirhams (MAD) along with the currency and active plan.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
GEThttps://api.envoisms.ma/v1/billing/balance
curl -X GET "https://api.envoisms.ma/v1/billing/balance" \
  -H "Authorization: Bearer smr_xxx"
{
  "balance_mad": 1492.5,
  "currency": "MAD",
  "plan": "croissance"
}
DocsMachine Payments/machine-payments/status
GEThttps://api.envoisms.ma/v1/machine-payments/status
Bearer Auth

Machine payments status

Reports whether machine payments (MPP / HTTP 402) are active on this instance. An agent can check this before attempting a paid call.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
Notes d'implémentation
  • When "enabled" is false, any paid call returns 503 instead of 402.
GEThttps://api.envoisms.ma/v1/machine-payments/status
curl -X GET "https://api.envoisms.ma/v1/machine-payments/status" \
  -H "Authorization: Bearer smr_xxx"
{
  "enabled": true,
  "deposit_usd": 5.00,
  "credit_mad": 49.65,
  "networks": ["base", "tempo"],
  "permissions": ["send", "status", "balance", "verify"]
}
DocsMachine Payments/messages
POSThttps://api.envoisms.ma/v1/messages
Bearer Auth

Payment challenge (HTTP 402)

Any paid endpoint called without an API key returns a 402 with a WWW-Authenticate: Payment header carrying a signed challenge and a USDC deposit address. The agent deposits exactly deposit_usd USDC to one of the addresses, then retries the call with a Payment credential.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
Notes d'implémentation
  • The "request" field is canonical JSON (RFC 8785) base64url-encoded without padding. It contains: amount, currency, networks[], recipients{}, paymentIntent, resource, credit{}.
  • IP deduplication: if an active challenge exists for the same IP, the same challenge is returned rather than a new deposit address.
  • Rate limit: 5 challenges per minute per IP. Beyond that, the 402 is returned without a deposit address.
POSThttps://api.envoisms.ma/v1/messages
curl -X POST "https://api.envoisms.ma/v1/messages" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+212612345678",
    "message": "Votre code de validation est 849204",
    "from": "MaBoutique",
    "channel": "whatsapp"
  }'
HTTP/1.1 402 Payment Required
WWW-Authenticate: Payment id="chal_01jxyz", realm="envoisms",
  method="stripe", intent="session",
  request="<base64url-JCS-encoded JSON>",
  expires="2026-09-25T21:45:00.000Z",
  description="EnvoiSMS pay-per-use API access."

{
  "type": "https://paymentauth.org/problems/payment-required",
  "title": "Payment Required",
  "status": 402,
  "detail": "Payment is required.",
  "challengeId": "chal_01jxyz"
}
DocsMachine Payments/messages (+ /v1/verify, /v1/waba)
POSThttps://api.envoisms.ma/v1/messages (+ /v1/verify, /v1/waba)
Bearer Auth

Redeem a payment credential

After depositing USDC funds, the agent retries the original paid endpoint (e.g. POST /v1/messages) replacing Authorization: Bearer with Authorization: Payment <credential>. The worker validates the Stripe deposit server-side, credits a machine account, and returns the scoped API key in the X-Machine-Session-Key response header.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
Notes d'implémentation
  • The credential is canonical JSON base64url: {"challenge":{"id":"chal_01jxyz","method":"stripe","intent":"session"},"source":"<wallet-address>","payload":{}}.
  • The returned key is shown once only. Idempotent: an agent that loses its key can call again — the same deposit returns a new key to the same account.
  • Returns 402 if the Stripe deposit has not yet reached "succeeded" status. The agent should wait for on-chain confirmation (typically 1–2 minutes) before retrying.
POSThttps://api.envoisms.ma/v1/messages (+ /v1/verify, /v1/waba)
curl -X POST "https://api.envoisms.ma/v1/messages  (+ /v1/verify, /v1/waba)" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{}'
{
  "key": "smr_live_xxxxxxxxxxxxxxxxxxxx",
  "permissions": ["send", "status", "balance", "verify"],
  "credit_mad": 49.65,
  "receipt": "<base64url-encoded receipt>",
  "account": {
    "id": "acct_01jxyz",
    "plan": "starter",
    "balance_mad": 49.65
  }
}
DocsAnalytics/analytics
GEThttps://api.envoisms.ma/v1/analytics
Bearer Auth

Usage statistics

Retrieves key metrics about your sends (total volumes, delivery rate, and billed costs).

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
GEThttps://api.envoisms.ma/v1/analytics
curl -X GET "https://api.envoisms.ma/v1/analytics" \
  -H "Authorization: Bearer smr_xxx"
{
  "summary": {
    "total": 12840,
    "delivered": 12570,
    "delivery_rate": 97.9,
    "cost_mad": 2663.1
  }
}
DocsAPI Keys/api-keys
GEThttps://api.envoisms.ma/v1/api-keys
Bearer Auth

List API keys

Retrieves the list of all your active or revoked API keys.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
GEThttps://api.envoisms.ma/v1/api-keys
curl -X GET "https://api.envoisms.ma/v1/api-keys" \
  -H "Authorization: Bearer smr_xxx"
{
  "data": [
    {
      "id": "key_e84c...",
      "name": "Production Server",
      "key_prefix": "smr_a8f7d6c5",
      "sandbox": false,
      "ip_whitelist": [
        "196.200.1.4"
      ],
      "rate_limit": 100,
      "permissions": [
        "send",
        "verify",
        "status"
      ],
      "active": true,
      "last_used_at": "2026-06-15T10:30:00Z",
      "created_at": "2026-05-01T08:00:00Z"
    }
  ]
}
DocsAPI Keys/api-keys
POSThttps://api.envoisms.ma/v1/api-keys
Bearer Auth

Create an API key

Generates a new secure API token with specific permissions and restrictions.

Request Body
FieldTypeRequirementDescription
namestringOptionalLabel to identify the key (default: "API key").
permissionsarrayOptionalGranted rights (send, verify, status, balance, contacts, campaigns, webhooks, billing, analytics, api_keys, optouts, sender_id).
ip_whitelistarrayOptionalList of IP addresses allowed to make requests with this key.
rate_limitintegerOptionalMaximum requests/min limit (10 to 2000). Default by plan: Starter 60, Business 180, Pro 500, Enterprise 1,200.
sandboxbooleanOptionaltrue generates a test key prefixed env_test_. Requests are validated and recorded, but no message is actually sent and nothing is billed. A key's mode is permanent: to change it, create a new key.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
POSThttps://api.envoisms.ma/v1/api-keys
curl -X POST "https://api.envoisms.ma/v1/api-keys" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production Server",
    "permissions": [
      "send",
      "verify",
      "status"
    ],
    "ip_whitelist": [
      "196.200.1.4"
    ],
    "rate_limit": 100
  }'
{
  "id": "key_e84c...",
  "name": "Production Server",
  "key_prefix": "smr_a8f7d6c5",
  "api_key": "smr_a8f7d6c5b4a3...",
  "sandbox": false,
  "permissions": [
    "send",
    "verify",
    "status"
  ],
  "rate_limit": 100,
  "warning": "The full API key is shown once. Store it securely."
}
DocsAPI Keys/api-keys/:id
PATCHhttps://api.envoisms.ma/v1/api-keys/:id
Bearer Auth

Update an API key

Updates the permissions, IP restrictions or activation state of an API key.

Request Body
FieldTypeRequirementDescription
namestringOptionalNew name.
permissionsarrayOptionalNew list of permissions.
ip_whitelistarrayOptionalNew list of allowed IP addresses.
rate_limitintegerOptionalNew per-minute rate limit.
activebooleanOptionalEnable or suspend the key.
HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
PATCHhttps://api.envoisms.ma/v1/api-keys/:id
curl -X PATCH "https://api.envoisms.ma/v1/api-keys/:id" \
  -H "Authorization: Bearer smr_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Backup Server",
    "active": true
  }'
{
  "updated": true,
  "id": "key_e84c..."
}
DocsAPI Keys/api-keys/:id
DELETEhttps://api.envoisms.ma/v1/api-keys/:id
Bearer Auth

Revoke an API key

Permanently revokes an API key to prevent it from authenticating requests.

HTTP Headers
Authorization: Bearer env_test_d...
Content-Type: application/json
Accept: application/json
DELETEhttps://api.envoisms.ma/v1/api-keys/:id
curl -X DELETE "https://api.envoisms.ma/v1/api-keys/:id" \
  -H "Authorization: Bearer smr_xxx"
{
  "revoked": true,
  "id": "key_e84c..."
}

Webhooks Guide

Signed and secure delivery callbacks.

EnvoiSMS.ma posts JSON events to your server's HTTPS URL with an X-EnvoiSMS.ma-Signature header to authenticate the sender.

message.sent

The message was accepted by the operator's network.

message.delivered

The message was successfully delivered to the recipient (SMS or WhatsApp).

message.read

The WhatsApp message was opened and read by the recipient (blue checkmarks).

message.failed

Delivery failed at the routing or submission stage (rejection, routing error).

message.undeliverable

The network confirmed the message cannot be delivered (non-existent number, expired in the operator queue).

message.fallback

Cascade (cascade: true): the message moves to the next channel (e.g. WhatsApp → SMS) because the first refused or reported the message failed, or sent no delivery report within cascade_timeout. channel is the new channel, previous_channel the old one, error_code and error_message the reason.

message.inbound

A recipient replied to one of your SMS or WhatsApp messages (inbound).

message.flow_response

A user completed and submitted a native WhatsApp Flow in-chat form.

message.location

A user shared their GPS location pin on WhatsApp.

lead.qualified

AtlasAI™ completed the BANT qualification of a lead on WhatsApp.

contact.optout

A recipient unsubscribed (STOP keyword or equivalent).

Signature Validation
import crypto from 'node:crypto';

// La signature arrive dans le header X-EnvoiSMS-Signature
// (format "sha256=<hex>"), l'événement dans X-EnvoiSMS-Event.
// Corps livré : { "event": "...", "data": { ... }, "timestamp": "..." }
export function verifySignature(body: string, sig: string, secret: string) {
  const hmac = crypto.createHmac('sha256', secret)
    .update(body)
    .digest('hex');

  return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from('sha256=' + hmac));
}
OpenAPI Spec

Errors

API error response structure.

All API errors return an appropriate HTTP code (4xx or 5xx) along with a predictable JSON envelope containing the error code and description.

{
  "error": {
    "code": "INVALID_PHONE",
    "message": "to must be E.164 format, for example +212612345678",
    "docs": "https://envoisms.ma/docs#errors"
  }
}
UNAUTHORIZED

Missing or invalid API key.

INSUFFICIENT_BALANCE

Insufficient balance to perform the send.

INVALID_PHONE

The phone number is not in E.164 format.

WHATSAPP_NOT_CONNECTED

No WhatsApp Business number connected: connect yours in the dashboard to send on WhatsApp. For an ordinary text, omit channel (SMS is the default) — nothing was charged.

OUT_OF_24H_WINDOW

Free-form WhatsApp message to a contact who has not written to you in the last 24 hours. Send an approved template. Nothing was charged.

WHATSAPP_ONLY_FIELD

A WhatsApp-only field (reaction, contacts, location, sticker, context) was sent on another channel.

CONFLICTING_MESSAGE_TYPES

A message carries one content type: reaction, contacts, location or sticker combine with nothing else.

CASCADE_NOT_SUPPORTED

cascade is not possible with a reaction, contacts, a location or a sticker: there is no SMS equivalent.

INVALID_REACTION

reaction needs the target message_id (wamid) and a single emoji (or an empty string to remove the reaction).

INVALID_CONTEXT

context needs the message_id (wamid) of the quoted message, and cannot be combined with reaction.

INVALID_CONTACTS

contacts must be an array of 1 to 20 cards, each with name.formatted_name; the message names the faulty card.

INVALID_LOCATION

location requires numeric latitude (-90 to 90) and longitude (-180 to 180); name and address are optional.

INVALID_MEDIA

A WhatsApp media object takes either "id" or "link" (an https URL), never both; no caption on audio and sticker.

META_WINDOW_EXPIRED

WhatsApp failure (131047): the 24-hour service window was closed. Send an approved template or an SMS.

META_UNDELIVERABLE

WhatsApp failure (131026): the number is not reachable on WhatsApp. Not retried automatically.

META_MARKETING_LIMIT

WhatsApp failure (131049): per-recipient marketing message limit. Do not resend right away.

META_RATE_LIMITED

WhatsApp failure (130429 number throughput, or 131056 as META_PAIR_RATE_LIMITED: too many messages to this contact) after 3 automatic attempts.

META_POLICY_BLOCKED

WhatsApp failure (368; also META_ACCOUNT_LOCKED 131031, META_PAYMENT_ISSUE 131042, META_PHONE_NOT_REGISTERED 133010): sending number or account restricted. Check WhatsApp Manager.

META_TEMPLATE_NOT_FOUND

WhatsApp failure (132001; also META_TEMPLATE_PARAM_MISMATCH 132000, META_TEMPLATE_PARAM_FORMAT 132012): template missing in that language, or wrong variables.

RATE_LIMITED

Per-minute request limit exceeded. Honor the Retry-After header.

INVALID_IDEMPOTENCY_KEY

The Idempotency-Key header exceeds 255 characters.

IDEMPOTENCY_IN_FLIGHT

The original request carrying this Idempotency-Key is still in flight. Retry in a moment.

IDEMPOTENCY_KEY_REUSED

This Idempotency-Key was already used with a different request body. Use a new key.

INVALID_CHANNEL

The requested channel does not exist. Valid channels: sms, whatsapp, telegram, voice, rcs.

CHANNEL_NOT_CONFIGURED

The requested channel is not currently available on the platform.

CHANNEL_DISABLED

The requested channel is disabled on the platform.

FORBIDDEN

The API key lacks the permission required for this action, or the account is suspended.

SENDER_ID_TOO_LONG

The Sender ID exceeds the 11-character limit.

SENDER_ID_INVALID

The Sender ID contains disallowed characters.

SENDER_ID_NOT_APPROVED

The Sender ID has not yet been approved by the carriers.

SENDER_ID_PENDING

The Sender ID is pending approval.

SENDER_ID_REJECTED

The Sender ID was rejected by the carriers.

SENDER_ID_GENERIC

The Sender ID is a generic header (INFO, ALERT, SERVICE…), not a brand. Use your brand name or omit sender_id.

SENDER_ID_PROTECTED_BRAND

The Sender ID names or resembles a protected institution (bank, carrier, public body). Reserved to accounts whose registration for that name was approved.

SENDER_ID_NOT_REGISTERED

The Sender ID was never registered on this account and the account has no completed top-up yet: sending is limited to your own verified number.

TRIAL_RESTRICTED_DESTINATION

Free trial account: test sends are restricted to your own verified phone number. Complete a first top-up to send to other recipients.

MISSING_FIELD

A required field is missing from the request.

CASCADE_TIMEOUT

The first channel timed out; switching to the secondary channel (cascade).

UPSTREAM_ERROR

Delivery error at the operator or gateway level.

OPTED_OUT

The number opted out of your communications (STOP). Sending is forbidden.

SPAM_OR_PHISHING_DETECTED

The message contains a link identified as phishing, or speaks as a bank, carrier or public body. The send was refused and the account suspended pending review.

CONTENT_BLOCKED

The message content was blocked by the anti-abuse screen and the account is suspended pending review. Contact support.

INVALID_CODE

The submitted OTP code is incorrect. The error message states how many attempts remain.

EXPIRED_CODE

The OTP code has expired. Request a new one via /v1/verify/send.

MAX_ATTEMPTS

Maximum number of verification attempts exceeded. The session is closed.

STRIPE_ERROR

The payment page could not be created on our side. Nothing was charged — retry in a moment.

INTERNAL_ERROR

Internal error on our side. Retry; contact support if the problem persists.

INVALID_PURPOSE

purpose must be marketing, transactional, otp or service.

INVALID_STATUS

status must be granted or withdrawn.

INVALID_CHANNEL

channel must be whatsapp, sms or any.

INVALID_SOURCE

source must be api, form, import or dashboard.

INVALID_EVIDENCE

evidence must be a JSON object under 4 KB.

INVALID_DATE

A timestamp is not ISO 8601, or recorded_at is in the future.

Limits

Constraints and quotas in the production environment.

OpenAPI YAML
API Requests

60 to 1,200 requests / minute per key, by plan (Starter 60, Business 180, Pro 500, Enterprise 1,200), raisable on request.

Message Size

1600 characters maximum per message.

Bulk Batch

Up to 10,000 messages per API call.

Log Retention

90 days for detailed reports.