Get started

Quickstart

Create an account, generate a sandbox key, register a webhook, and send your first message in a few minutes.

Updated November 12, 2026View as Markdown

This walkthrough uses the sandbox: nothing is billed and no real recipient is contacted. The examples run in your terminal; each block is available in curl, Node.js, Python, and PHP (your language choice is remembered).

First, set the API address:

bash
export INBOX_API="https://YOUR-HOST/api/v1"

Create your account#

Creating an account also creates its first member (the owner) and opens a session.

bash
curl -X POST "$INBOX_API/auth/signup" \
  -H "Content-Type: application/json" \
  -d '{
    "accountName": "Acme Software",
    "email": "dev@acme.com",
    "password": "un-mot-de-passe-solide"
  }'
Response · 201 Created
{
  "accountId": "bbfa1ecb-9483-4158-8e9a-4437e512b14b",
  "memberId": "e8878e87-7817-4773-8346-fc26a2433aa6",
  "sessionToken": "ses_91bb0d22558b4575a110844d944cca5d8b05e72328b",
  "expiresAt": "2026-12-12T08:33:45.098Z"
}

The password must be at least 10 characters long. Keep the sessionToken: it is the console key, valid for 30 days.

bash
export INBOX_SESSION="ses_…"

Create an app#

An app has two environments: sandbox for testing and production for sending to real recipients. They are strictly separate.

bash
curl -X POST "$INBOX_API/apps" \
  -H "Authorization: Bearer $INBOX_SESSION" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My CRM"
  }'
Response · 201 Created
{
  "id": "8b2717b6-be9e-42c7-8889-6e3c6ea4cbe3",
  "name": "My CRM",
  "createdAt": "2026-11-12T08:33:45.324Z",
  "environments": [
    {
      "id": "7d3d2aa8-d312-48d7-894c-25b906dfa11a",
      "kind": "sandbox"
    },
    {
      "id": "badd0f25-05da-4716-8c28-f2fe1baba427",
      "kind": "production"
    }
  ]
}

Generate a sandbox key#

Choose its scopes: a key can only do what you grant it.

bash
curl -X POST "$INBOX_API/apps/APP_ID/keys" \
  -H "Authorization: Bearer $INBOX_SESSION" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Local dev",
    "environment": "sandbox",
    "scopes": [
      "messages:send",
      "messages:read",
      "connections:manage",
      "connections:read",
      "templates:manage",
      "webhooks:manage",
      "usage:read"
    ]
  }'
Response · 201 Created
{
  "id": "5ef37bc9-8aac-43ef-81b1-6bb3bdb66e78",
  "name": "Local dev",
  "prefix": "pk_test_bwcA3X-A",
  "environment": "sandbox",
  "scopes": [
    "messages:send",
    "messages:read",
    "connections:manage",
    "connections:read",
    "templates:manage",
    "webhooks:manage",
    "usage:read"
  ],
  "lastUsedAt": null,
  "revokedAt": null,
  "createdAt": "2026-11-12T08:33:45.382Z",
  "secret": "pk_test_26f602c48c47ef280792fd3a506473688df70b80118"
}
bash
export INBOX_KEY="pk_test_…"

Register your customer's business#

A connected business represents your customer, the owner of a WhatsApp Business account. externalRef is your own identifier for that customer.

bash
curl -X POST "$INBOX_API/connected-businesses" \
  -H "Authorization: Bearer $INBOX_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Cabinet Dentaire Zokem",
    "externalRef": "client-42"
  }'
Response · 201 Created
{
  "id": "39e2803f-79e7-4e99-8da0-1c15991f5985",
  "name": "Cabinet Dentaire Zokem",
  "externalRef": "client-42",
  "createdAt": "2026-11-12T08:33:45.469Z"
}

Connect their WhatsApp account#

Your customer completes Meta's Embedded Signup in the browser; you receive a code, a wabaId, and a phoneNumberId, which you pass to the API. See Connect a WhatsApp account.

bash
curl -X POST "$INBOX_API/connected-businesses/BUSINESS_ID/waba-connections" \
  -H "Authorization: Bearer $INBOX_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "AQBx7…",
    "wabaId": "102290129340398",
    "phoneNumberId": "106540352242922",
    "mode": "cloud_api"
  }'
Response · 201 Created
{
  "id": "b76a1f98-53d6-4078-8d28-6406482daabb",
  "wabaId": "102290129340398",
  "status": "active",
  "mode": "cloud_api",
  "phoneNumber": {
    "id": "4d8e654e-d744-4bf3-8211-9ceffcccfe87",
    "phoneNumberId": "106540352242922",
    "displayNumber": "+237 6 52242922",
    "verifiedName": "Cabinet Test",
    "qualityRating": "GREEN"
  }
}

Note the number's id (phoneNumber.id): it identifies the sending number.

Receive events#

Register your webhook endpoint. The signing secret is shown only once.

bash
curl -X POST "$INBOX_API/webhook-endpoints" \
  -H "Authorization: Bearer $INBOX_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.example.com/webhooks/inbox",
    "events": [
      "message.received",
      "message.delivered",
      "message.failed"
    ]
  }'
Response · 201 Created
{
  "id": "bc06ae32-7564-46d5-89fb-a9869019aa8e",
  "url": "https://api.example.com/webhooks/inbox",
  "events": [
    "message.received",
    "message.delivered",
    "message.failed"
  ],
  "status": "active",
  "consecutiveFailures": 0,
  "disabledReason": null,
  "createdAt": "2026-11-12T08:33:45.704Z",
  "secret": "whsec_b4ac869893324714f88bfd25026b3e2e2891bb8c087"
}

Check that your server responds correctly, using a signed test event:

bash
curl -X POST "$INBOX_API/webhook-endpoints/ID/test" \
  -H "Authorization: Bearer $INBOX_KEY"
Response · 200 OK
{
  "ok": true,
  "statusCode": 200,
  "durationMs": 3,
  "error": null
}

Send your first message#

bash
curl -X POST "$INBOX_API/messages" \
  -H "Authorization: Bearer $INBOX_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: rdv-2026-11-14-client-42" \
  -d '{
    "phoneNumberId": "4d8e654e-d744-4bf3-8211-9ceffcccfe87",
    "to": "+237600000000",
    "type": "template",
    "template": {
      "name": "appointment_reminder",
      "language": "en",
      "components": [
        {
          "type": "body",
          "parameters": [
            {
              "type": "text",
              "text": "Awa"
            },
            {
              "type": "text",
              "text": "November 14 at 10 am"
            }
          ]
        }
      ]
    }
  }'
Response · 202 Accepted
{
  "id": "d5ad1e3e-6ab9-4f7f-8e4e-b17104a7bd00",
  "direction": "outbound",
  "status": "queued",
  "type": "template",
  "phoneNumberId": "4d8e654e-d744-4bf3-8211-9ceffcccfe87",
  "to": "237600000000",
  "waMessageId": null,
  "content": {
    "type": "template",
    "template": {
      "name": "appointment_reminder",
      "language": "en",
      "components": [
        {
          "type": "body",
          "parameters": [
            {
              "type": "text",
              "text": "Awa"
            },
            {
              "type": "text",
              "text": "November 14 at 10 am"
            }
          ]
        }
      ]
    }
  },
  "error": null,
  "createdAt": "2026-11-12T08:33:45.855Z",
  "sentAt": null,
  "deliveredAt": null,
  "readAt": null
}

The response is immediate (202, status queued): the actual send is asynchronous. Track the status with GET /messages/{id} or, better, with your webhooks.

Simulate a customer reply#

Without a phone, make a message "arrive": your message.received webhook fires exactly as it does in production.

bash
curl -X POST "$INBOX_API/sandbox/simulate/inbound-message" \
  -H "Authorization: Bearer $INBOX_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumberId": "4d8e654e-d744-4bf3-8211-9ceffcccfe87",
    "from": "+237611111111",
    "text": "Hello, I'\''d like to reschedule my appointment"
  }'
Response · 201 Created
{
  "messageId": "3f3baf5a-f93e-42e8-8263-ce62dc4aadd6",
  "from": "237611111111",
  "status": "received",
  "simulated": true
}

What's next#