Channels
Channels are the WhatsApp connections under your account. The full lifecycle lives in Concepts → Channels; this page covers the wire shape.
List channels
Returns every channel owned by the authenticated customer, newest first. Soft-deleted channels are excluded.
curl -s "$WHATISUP_API/v1/channels" \
-H "Authorization: Bearer $WHATISUP_API_KEY"{
"data": [
{
"id": "8d653c66-e4ff-43ee-97da-3de5ad5680d4",
"customer_id": "cust_01J6...",
"name": "primary",
"phone_number": "5511999999999",
"status": "connected",
"last_seen_at": "2026-05-01T12:34:56.000Z",
"metadata": {},
"created_at": "2026-04-12T08:11:32.000Z",
"updated_at": "2026-05-01T12:34:56.000Z"
}
]
}Create channel
| Field | Type | Required | Notes |
|---|---|---|---|
| name | string | required | |
| metadata | object | optional | |
| issue_scoped_key | boolean | optional |
curl -sX POST "$WHATISUP_API/v1/channels" \
-H "Authorization: Bearer $WHATISUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"support-line","metadata":{"team":"support"}}'The response is a Channel object. Status starts at pending — the WhatsApp connection isn't allocated yet. Fetch the QR to start one.
| Field | Type | Required | Notes |
|---|---|---|---|
| id | string · uuid | required | |
| customer_id | string · uuid | required | |
| name | string | required | |
| phone_number | string | required · nullable | |
| status | `pending` \| `qr` \| `connecting` \| `reconnecting` \| `connected` \| `disconnected` \| `logged_out` \| `stopped_by_user` \| `disabled_by_admin` \| `error` \| `failed` | required | |
| last_seen_at | string · ISO 8601 | required · nullable | |
| metadata | object | required | |
| health | object | optional · nullable | |
| created_at | string · ISO 8601 | required | |
| updated_at | string · ISO 8601 | required |
Get one channel
Returns the single channel. 404s for channels that belong to a different customer (we never leak a "wrong tenant" 403; both look like "not found").
Get the current QR
Lazily starts the WhatsApp connection if one isn't running, then waits up to ~10s for a QR. Returns data:image/png;base64,… plus the raw wire-format string (handy if you want to render with your own QR library) and an expires_at.
{
"qr": "data:image/png;base64,iVBORw0KGgo...",
"raw": "2@dPBI4Tg9Mc...,vlD4Pmkr...",
"expires_at": "2026-05-01T12:35:25.000Z"
}The dashboard's Pair flow uses SSE instead — it gets the QR pushed without polling.
Soft-delete
Stops the socket, wipes auth-state, marks the row deleted. Webhook deliveries that referenced this channel stay queryable for forensics.
curl -sX DELETE "$WHATISUP_API/v1/channels/8d653c66-e4ff-43ee-97da-3de5ad5680d4" \
-H "Authorization: Bearer $WHATISUP_API_KEY"Returns 204 No Content.
Number health
How likely WhatsApp is to restrict the number: level (good, warning, danger, restricted), the reasons, the last 7 days' new contacts and reply rate, and what auto-protect is doing about it. Recomputed every 15 minutes; a WhatsApp restriction shows up at once. The channel.health webhook fires when the level changes. See Keeping numbers healthy.
| Field | Type | Required | Notes |
|---|---|---|---|
| level | `good` \| `warning` \| `danger` \| `restricted` | required | |
| reasons | array<`low_reply_rate` \| `very_low_reply_rate` \| `new_number` \| `whatsapp_warning` \| `whatsapp_restricted`> | required | |
| new_contacts_7d | number | required | |
| reply_rate_7d | number | required · nullable | |
| age_days | number | required | |
| auto_protect | object | required | |
| checked_at | string · ISO 8601 | required · nullable | |
| channel_id | string · uuid | required |
WhatsApp limits
What WhatsApp itself reports for the number: new_chats (its quota for starting chats with people who never wrote to it) and restriction (a temporary block on messaging new people, with ends_at). The channel.limits webhook fires when either changes. The gateway never refuses a send over them — a send to someone new while they apply carries a warnings entry instead.
| Field | Type | Required | Notes |
|---|---|---|---|
| new_chats | object | required · nullable | |
| restriction | object | required · nullable | |
| checked_at | string · ISO 8601 | required · nullable | |
| channel_id | string · uuid | required |
Send pacing
A random pause between this number's sends and "typing…" before each, plus the auto-protect switch (off unless you turn it on). While a number is paced its sends are queued (202, status: "queued") and go out one at a time.
| Field | Type | Required | Notes |
|---|---|---|---|
| auto_protect | boolean | optional | |
| min_gap_seconds | number | optional | |
| max_gap_seconds | number | optional | |
| typing | boolean | optional |
| Field | Type | Required | Notes |
|---|---|---|---|
| auto_protect | boolean | optional | |
| min_gap_seconds | number | optional | |
| max_gap_seconds | number | optional | |
| typing | boolean | optional | |
| channel_id | string · uuid | required |
Proxy
Routes the number's WhatsApp traffic through your SOCKS5, SOCKS4 or HTTP(S) proxy. PUT tests the proxy first (test: false skips it) and saves nothing when the test fails; POST …/test checks a url without saving it, or the saved proxy. Up to 10 tests per minute per account (429 proxy_test_limit). The password is stored encrypted and never returned.
| Field | Type | Required | Notes |
|---|---|---|---|
| url | string | required | |
| test | boolean | optional |
| Field | Type | Required | Notes |
|---|---|---|---|
| channel_id | string · uuid | required | |
| configured | boolean | required | |
| scheme | `socks5` \| `socks5h` \| `socks4` \| `http` \| `https` | required · nullable | |
| host | string | required · nullable | |
| port | number | required · nullable | |
| username | string | required · nullable | |
| has_password | boolean | required | |
| updated_at | string · ISO 8601 | required · nullable | |
| last_check | object | required · nullable |
| Field | Type | Required | Notes |
|---|---|---|---|
| ok | boolean | required | |
| egress_ip | string | required · nullable | |
| country | string | required · nullable | |
| latency_ms | number | required · nullable | |
| error | string | required · nullable | |
| warnings | array<`rotating_ip` \| `country_mismatch`> | optional | |
| checked_at | string · ISO 8601 | required |