Getting Started

Base URL

Production: https://wpp-cloud.com/v1

All code samples in these guides use https://api.example.com as a placeholder — substitute the base URL above (or your own deployment's URL). Every v1 endpoint is rooted at /v1.


Prerequisites

To integrate against this API you need exactly three things:

Requirement What it is How to get it
Base URL The API root for all requests Documented above: https://wpp-cloud.com/v1
API key Organization-scoped Bearer token (sk_org_...) Issued by the platform administrator — request it from them. Keys cannot be self-created through the public API.
phoneId Internal UUID of the WhatsApp line you will send/receive through Connect a phone via the onboarding flow, or look it up if already connected (see below)

1. Get an API key

API keys are scoped to an organization and are issued by the platform administrator — creating organizations and keys is not part of the public API. Ask your administrator for:

Verify the key works before anything else:

curl https://wpp-cloud.com/v1/organizations/me \
  -H "Authorization: Bearer sk_org_..."

A 200 with your organization's data.id and data.name confirms the key is valid. A 401 means the key is wrong or was revoked.

2. Get the phoneId

The phoneId is the internal UUID of a connected phone number — it goes in every messaging path (/v1/phones/{phoneId}/...). There are two ways to obtain it:

A. The phone is already connected — list your WABAs; each one includes its phone numbers:

curl https://wpp-cloud.com/v1/wabas \
  -H "Authorization: Bearer sk_org_..."
{
  "data": [
    {
      "id": "770e8400-e29b-41d4-a716-446655440002",
      "wabaId": "987654321098765",
      "phoneNumbers": [
        {
          "id": "660e8400-e29b-41d4-a716-446655440001",
          "phoneNumberId": "123456789012345",
          "phoneNumber": "+54 9 11 5555-1234",
          "isRegistered": true,
          "qualityRating": "GREEN",
          "messagingLimit": "1K"
        }
      ]
    }
  ]
}

⚠️ Naming trap: in this response, the phone's id field (UUID) is the phoneId you use in API paths. The field literally named phoneNumberId is Meta's numeric ID — do not use it in /v1/phones/{phoneId}/... URLs.

B. The phone is not connected yet — run the onboarding flow:

  1. POST /v1/onboarding-links with a name (and optionally redirectUrl, webhookUrl) — see the Onboarding Links guide
  2. Send the returned hostedUrl to whoever owns the WhatsApp number; they complete Meta's Embedded Signup in the browser
  3. When they finish, fetch GET /v1/onboarding-links/{id} — the response now includes phoneNumberId (the internal UUID, i.e. your phoneId), phoneNumber, and wabaId. Alternatively, listen for the phone_number.connected webhook event, which carries the same IDs

3. Verify the line is ready

Before wiring production traffic, confirm the phone can actually send and receive:

curl https://wpp-cloud.com/v1/phones/{phoneId}/health \
  -H "Authorization: Bearer sk_org_..."

Check data.capabilities.canSendMessages and data.capabilities.canReceiveMessages — both should be true. Then send yourself a test message:

curl -X POST https://wpp-cloud.com/v1/phones/{phoneId}/messages \
  -H "Authorization: Bearer sk_org_..." \
  -H "Content-Type: application/json" \
  -d '{"type": "text", "to": "5491155551234", "text": "Test from the API"}'

A 200 with a wamid means the full chain works.


Authentication

All API requests require a Bearer token in the Authorization header:

Authorization: Bearer <YOUR_API_KEY>

API keys are scoped to an organization and are issued by the platform administrator (see Prerequisites above) — request yours from them.

Error responses for invalid or missing authentication:


Core Concepts

Data Model

Organization
├── API Keys
├── Onboarding Links
├── Shared Inbox Links
├── Outgoing Webhooks
└── WABAs (WhatsApp Business Accounts)
    └── Phone Numbers
        └── Conversations
            └── Messages

IDs

The API uses two kinds of IDs:

ID Type Format Example
Internal IDs UUID v4 550e8400-e29b-41d4-a716-446655440000
Meta IDs Numeric string 123456789012345

Most endpoints use internal UUIDs. Meta IDs appear in webhook payloads and when interfacing with Meta's Graph API.

When handling message webhooks, store data.contactIdentifier as your contact key and send replies to data.contactReplyTo. data.contactPhone is legacy display data and can be null. See Migrating from Phone to Contact Identity.


Quick Start

1. Connect a WhatsApp Number

Create an onboarding link and send it to your end-user:

curl -X POST https://api.example.com/v1/onboarding-links \
  -H "Authorization: Bearer sk_org_..." \
  -H "Content-Type: application/json" \
  -d '{"name": "Acme Corp", "redirectUrl": "https://myapp.com/callback"}'

Send the hostedUrl from the response to your end-user. They complete Meta's Embedded Signup in the browser. See the Onboarding Links guide for the full flow.

2. Check Phone Health

Before sending production traffic, fetch the phone health snapshot:

curl https://api.example.com/v1/phones/{phoneId}/health \
  -H "Authorization: Bearer sk_org_..."

Look at data.capabilities.canReceiveMessages:

This endpoint also triggers an asynchronous resync against Meta in the background. If the local state was stale, a subsequent call will reflect the refreshed values.

3. Send a Message

Before sending outbound traffic, confirm data.capabilities.canSendMessages is true:

curl -X POST https://api.example.com/v1/phones/{phoneId}/messages \
  -H "Authorization: Bearer sk_org_..." \
  -H "Content-Type: application/json" \
  -d '{"type": "text", "to": "5491155551234", "text": "Hello!"}'

See the Sending Messages guide for all message types.

4. Optionally Expose a Shared Inbox

If you want an external user to work inside the inbox UI without giving them your org API key, create a shared inbox link:

curl -X POST https://api.example.com/v1/shared-inbox-links \
  -H "Authorization: Bearer sk_org_..." \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumberId": "660e8400-e29b-41d4-a716-446655440001",
    "name": "Support access",
    "permissions": {
      "readInbox": true,
      "readDetails": true,
      "markRead": true,
      "sendText": true,
      "sendMedia": true,
      "sendTemplates": true,
      "sendAdvanced": false
    }
  }'

Send the publicUrl from the response to the external user. See the Shared Inbox Links guide for permissions, expiry, rotation, and revoke flows.

5. Receive Messages via Webhooks

Register a webhook to get notified of incoming messages and status updates:

curl -X POST https://api.example.com/v1/outgoing-webhooks \
  -H "Authorization: Bearer sk_org_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/whatsapp",
    "events": ["message.received", "message.sent"],
    "secret": "whsec_your_secret_key"
  }'

See the Webhooks guide for payload formats and signature verification.

6. Optionally Subscribe to Realtime Events

The same events delivered to webhooks are also pushed over a WebSocket, with identical payloads — ideal for live UIs (inboxes, dashboards) without polling:

websocat -H "Authorization: Bearer sk_org_..." wss://api.example.com/v1/realtime

Webhooks remain the guaranteed delivery channel (retries + logs); the WebSocket is the low-latency one. See the Realtime WebSocket guide for browser auth (ephemeral client secrets), filtering, keepalive, and reconnection.


Phone Readiness

The platform stores two related but different fields for each phone number:

Field Meaning
isRegistered The phone has been registered with WhatsApp / Cloud API
capabilities.canReceiveMessages The phone is ready for inbound traffic with the current app subscription and no blocking health issues
capabilities.canSendMessages The phone is healthy enough for outbound messaging
capabilities.canSendTemplates The phone is healthy enough for outbound template messaging

Use the capability that matches your integration. For inbound-only flows, canReceiveMessages is usually the right operational flag.


Response Format

All responses follow a consistent envelope:

Success (single resource):

{
  "data": { ... }
}

Success (list):

{
  "data": [ ... ],
  "meta": {
    "pagination": {
      "page": 1,
      "limit": 50,
      "total": 120,
      "hasMore": true
    }
  }
}

Error:

{
  "error": {
    "message": "Resource not found",
    "code": "NOT_FOUND"
  }
}

Error Codes

Code HTTP Status Description
VALIDATION_ERROR 400 Invalid request body or parameters
UNAUTHORIZED 401 Missing or invalid API key
FORBIDDEN 403 API key doesn't have access
NOT_FOUND 404 Resource not found
MESSAGE_SEND_FAILED 400 Meta rejected the send — the raw Meta error JSON is embedded in error.message (see Sending Messages — Error Handling)
MEDIA_UPLOAD_FAILED 400 Media upload was rejected (unsupported format, size limit)
PHONE_NOT_REGISTERED 400 The phone number is not registered on the Cloud API
INTERNAL_ERROR 500 Unexpected server error