Skip to content
Contents
Sending and receiving

Channels

A channel is a number you can send from, and the provider behind it is the only thing that differs between them. Choosing one is the first real decision in an integration, so this page states plainly what each provider does, what it costs you in risk, and what is not built yet.

Three providers

The send API is identical across all of them. What differs is how you get the number, what the number can do, and what happens to it over time.

ProviderAvailabilityDescription
sandboxAvailableA shared test number the platform owns. Instant, free, and restricted to recipients who have verified they own their number.
qrAvailableThe customer's own WhatsApp account, linked by scanning a QR code. Immediate, and unofficial — see the policy note below.
metaNot yet availableThe official WhatsApp Business Platform. No adapter is deployed, so creating one is refused with a validation error naming the providers this environment does support.

Endpoints

GET/v1/channelsPOST/v1/channelsGET/v1/channels/{channelId}GET/v1/channels/{channelId}/capabilitiesPATCH/v1/channels/{channelId}DELETE/v1/channels/{channelId}

Deleting a channel archives it rather than destroying it: the messages sent through it stay readable, and the operation is repeatable.

curl
curl https://whats.azzamkh.sa/api/v1/channels \
  -H "Authorization: Bearer $WA_API_KEY"

# 200 OK
# {
#   "data": [{
#     "id": "ch_...",
#     "name": "Sandbox",
#     "provider": "sandbox",
#     "status": "connected",
#     "phone_number": "+999...",
#     "is_default": true,
#     "capabilities": {
#       "send_text": true,
#       "send_media": false,
#       "send_local_template": true,
#       "send_meta_template": false,
#       "campaigns": false,
#       "max_throughput_mps": 2
#     },
#     "capabilities_refreshed_at": "2026-08-13T10:00:00.000Z"
#   }],
#   "page": { "next_cursor": null, "has_more": false }
# }

Channel status

A send is only accepted from a connected channel; every other status is refused with CHANNEL_NOT_CONNECTED.

StatusDescription
pendingCreated, and not yet connected to anything.
connectingThe provider is being contacted.
awaiting_scanA QR code is waiting to be scanned.
connectedReady. The only status a send is accepted from.
degradedConnected but unhealthy — the provider is unreachable or failing. Sends may still be accepted and may still fail.
disconnectedNo longer linked. A QR channel reaches this after a logout.
suspendedStopped by the platform.
archivedRetired by the workspace. Its history stays readable.

Capabilities

Each channel publishes what its provider can actually do. Read it rather than assuming — send_media is false everywhere today, and a capability a channel does not have is a 409, not a silent no-op.

Capabilitysandboxqr
send_texttruetrue
send_mediafalsefalse
send_local_templatetruetrue
send_meta_templatefalsefalse
campaignsfalsefalse
official_metatruefalse
business_initiated_templatesfalsefalse
message_statustruetrue
read_receiptstruetrue
coexistencefalsefalse

Throughput is published with them: 2 messages per second on a sandbox channel and 0.5 on a QR one.

A snapshot, not a live query

Capabilities come from a stored snapshot. Until one has been taken, the capabilities endpoint answers 409 with details.reason = "capabilities_not_refreshed" rather than guessing.

Sandbox

The sandbox is a pool of platform-owned test numbers, shared between workspaces. Allocating one creates a channel and a session; the session is what carries the verification and the expiry.

POST/v1/sandbox/sessionsPOST/v1/sandbox/sessions/{sessionId}/verifyGET/v1/sandbox/sessions/{sessionId}DELETE/v1/sandbox/sessions/{sessionId}

The rules that will surprise you

  • Every sandbox operation requires a test-mode API key. A live key is a 403.
  • You can only message a recipient who has proven ownership by sending the verification code from their own handset. Any other recipient is refused.
  • The allocation expires after 24 hours, after which sends are refused and you allocate again.
  • The pool is finite. When every healthy number is at capacity the allocation is a 503, not a queue.
  • You cannot pick a number. Allocation picks one for you, and never one already serving the same recipient for someone else.

The quickstart walks the whole flow end to end.

QR — the customer's own number

This is not the official WhatsApp path

QR linking drives WhatsApp's own multi-device protocol as an unofficial client. WhatsApp's policies restrict unauthorised clients, bulk messaging and automated messaging, and an account connected this way can be restricted or banned. No technical limit can make it safe — the platform will never describe QR as an official Meta integration, and will never claim an account cannot be banned.

The safety limit below is a platform policy chosen to be conservative. It is not a WhatsApp guarantee, and staying under it guarantees nothing.

A QR channel is created through the normal endpoint, then paired by scanning a code with the phone that owns the account.

curl
curl -X POST https://whats.azzamkh.sa/api/v1/channels \
  -H "Authorization: Bearer <access token>" \
  -H "X-Workspace-Id: ws_..." \
  -H "Content-Type: application/json" \
  -d '{ "name": "Support line", "provider": "qr" }'

# 201 Created  { "id": "ch_...", "provider": "qr", "status": "pending", ... }

# Pairing itself is a browser flow: POST .../qr/start, then read the codes
# from the SSE stream. Those four routes do NOT accept an API key.

The pairing flow

Pairing is a browser flow, and its four routes accept a user access token only — an API key is refused. The API records the intent; a worker holds the socket and produces the codes, which arrive over the event stream rather than in a poll response.

POST/v1/channels/{channelId}/qr/startGET/v1/channels/{channelId}/qr/statusGET/v1/channels/{channelId}/qr/streamPOST/v1/channels/{channelId}/qr/logout

The limits on a QR channel

LimitValueDescription
Safety limit200 / 24hOutbound messages per rolling window per channel. Exceeding it is a 429 with QR_SAFETY_LIMIT_REACHED.
Same-recipient rate6 / minMessages per minute to any one recipient.
Throughput0.5 msg/sThe rate the dispatcher will hand messages to the provider.
Code validity60sHow long one displayed QR code is valid before it is replaced.

Meta Cloud API

Not yet available

No Meta adapter is deployed. Creating a channel with that provider is refused with a 422 whose details.available lists the providers this environment does support. There is no onboarding endpoint and no template submission endpoint.

The API was designed so that this is a channel swap and not a rewrite: the send request, the message shape, the webhook events and the error codes are all provider-independent. When Meta arrives, an integration written against the sandbox or against QR changes the channel it names and nothing else.