API Reference
Complete reference for the WhatsScale API. Send messages, manage contacts, post stories, and automate your WhatsApp.
Getting Started
1Sign up at whatsscale.com/signup
2Connect your WhatsApp by scanning the QR code in the Dashboard
3Generate an API key from the Dashboard
4Use the API key in the X-Api-Key header for all requests
Authentication
All API requests require your API key in the header:
X-Api-Key: YOUR_API_KEY
Base URL:
https://proxy.whatsscale.com
Authentication & Sessions
Verify your API key and manage WhatsApp sessions.
/api/auth/testTest Authentication
Verify your API key is valid and see your account info.
curl -X GET https://proxy.whatsscale.com/api/auth/test \
-H "X-Api-Key: YOUR_API_KEY"{
"success": true,
"user": {
"id": "user-uuid",
"email": "[email protected]"
}
}/api/sessionsList Sessions
Get all WhatsApp sessions linked to your account. Session names are per account — use the `name` values this endpoint returns wherever a sample shows YOUR_SESSION. Example values in these docs are placeholders and will return 403.
curl -X GET https://proxy.whatsscale.com/api/sessions \
-H "X-Api-Key: YOUR_API_KEY"[
{
"name": "user_example0000000000000000000000_Ab12Cd34",
"status": "WORKING",
"me": {
"id": "[email protected]",
"pushName": "John"
}
}
]Status "WORKING" means WhatsApp is connected and ready.
Use the session "name" as the session parameter in all other API calls.
Contacts & Groups
Fetch WhatsApp contacts and groups for your connected session.
/api/:session/contactsList WhatsApp Contacts
Get all contacts synced from your WhatsApp.
| Name | Type | Required | Description |
|---|---|---|---|
| session | string | REQUIRED | Session name from /api/sessions |
curl -X GET https://proxy.whatsscale.com/api/YOUR_SESSION/contacts \
-H "X-Api-Key: YOUR_API_KEY"[
{
"id": "[email protected]",
"name": "John Doe",
"pushName": "John"
}
]Contact ID format: phone number + "@c.us" (e.g. [email protected]).
/api/:session/groupsList WhatsApp Groups
Get all groups you are a member of.
| Name | Type | Required | Description |
|---|---|---|---|
| session | string | REQUIRED | Session name from /api/sessions |
curl -X GET https://proxy.whatsscale.com/api/YOUR_SESSION/groups \
-H "X-Api-Key: YOUR_API_KEY"[
{
"id": "[email protected]",
"subject": "Team Chat",
"size": 12
}
]Group ID format: number + "@g.us".
Messaging
Send text, images, videos, documents, locations, and polls.
/api/sendTextSend Text Message
Send a plain text message to a contact, group, or channel.
| Name | Type | Required | Description |
|---|---|---|---|
| session | string | REQUIRED | Your session name |
| chatId | string | REQUIRED | Recipient: [email protected] or [email protected] |
| text | string | REQUIRED | Message text (max 4096 chars) |
curl -X POST https://proxy.whatsscale.com/api/sendText \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session": "YOUR_SESSION",
"chatId": "[email protected]",
"text": "Hello from WhatsScale!"
}'{
"key": {
"remoteJid": "[email protected]",
"fromMe": true,
"id": "3EB0..."
},
"status": "PENDING"
}/api/sendImageSend Image
Send an image via URL with an optional caption.
| Name | Type | Required | Description |
|---|---|---|---|
| session | string | REQUIRED | Your session name |
| chatId | string | REQUIRED | Recipient chat ID |
| file | string | REQUIRED | Public URL to the image |
| caption | string | OPTIONAL | Image caption (max 1024 chars) |
curl -X POST https://proxy.whatsscale.com/api/sendImage \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session": "YOUR_SESSION",
"chatId": "[email protected]",
"file": "https://example.com/photo.jpg",
"caption": "Check this out!"
}'{
"key": {
"remoteJid": "[email protected]",
"fromMe": true,
"id": "3EB0..."
},
"status": "PENDING"
}/api/sendVideoSend Video
Send a video via URL. Videos are processed asynchronously.
| Name | Type | Required | Description |
|---|---|---|---|
| session | string | REQUIRED | Your session name |
| chatId | string | REQUIRED | Recipient chat ID |
| file | string | REQUIRED | Public URL to the video |
| caption | string | OPTIONAL | Video caption (max 1024 chars) |
curl -X POST https://proxy.whatsscale.com/api/sendVideo \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session": "YOUR_SESSION",
"chatId": "[email protected]",
"file": "https://example.com/clip.mp4",
"caption": "Watch this!"
}'{
"jobId": "abc123-def456",
"status": "QUEUED",
"message": "Video queued for processing"
}Video sending is async. Poll GET /api/status/:jobId to check progress.
/api/sendDocumentSend Document
Send a document (PDF, DOCX, etc.) via URL.
| Name | Type | Required | Description |
|---|---|---|---|
| session | string | REQUIRED | Your session name |
| chatId | string | REQUIRED | Recipient chat ID |
| file | string | REQUIRED | Public URL to the document |
| filename | string | OPTIONAL | Display filename (auto-detected from URL if omitted) |
| caption | string | OPTIONAL | Document caption (max 1024 chars) |
curl -X POST https://proxy.whatsscale.com/api/sendDocument \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session": "YOUR_SESSION",
"chatId": "[email protected]",
"file": "https://example.com/report.pdf",
"filename": "Q4-Report.pdf",
"caption": "Here is the report"
}'{
"key": {
"remoteJid": "[email protected]",
"fromMe": true,
"id": "3EB0..."
},
"status": "PENDING"
}/api/sendAudioSend Audio / Voice Note
Send audio from a URL. By default this sends a native voice note (PTT) — a play-in-place bubble with a waveform. Pass voice:false to send the audio as a downloadable file attachment instead.
| Name | Type | Required | Description |
|---|---|---|---|
| session | string | REQUIRED | Your session name |
| chatId | string | REQUIRED | Recipient — contact (@c.us) or group (@g.us) |
| file | string | REQUIRED | Public URL to the audio file (mp3, m4a, aac, ogg, wav) |
| voice | boolean | OPTIONAL | true (default) sends a native voice note, transcoded to Opus automatically. false sends the audio as a file attachment. |
| filename | string | OPTIONAL | Display filename (auto-detected from URL if omitted). Ignored for voice notes. |
| caption | string | OPTIONAL | Caption (max 1024 chars). Only valid with voice:false — a voice note cannot have a caption. |
curl -X POST https://proxy.whatsscale.com/api/sendAudio \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session": "YOUR_SESSION",
"chatId": "[email protected]",
"file": "https://example.com/note.mp3"
}'{
"key": {
"remoteJid": "[email protected]",
"fromMe": true,
"id": "3EB0..."
},
"status": "PENDING"
}A voice note carries no caption — WhatsApp has no caption field on a PTT. Sending caption with voice:true is rejected rather than silently dropped.
Voice notes are transcoded to Opus server-side, so any common audio format works as a source.
Channels (@newsletter) are not supported by this endpoint — use a contact or group chatId.
Long clips take proportionally longer to transcode. Keep voice notes short; send long audio with voice:false instead.
/api/sendLocationSend Location
Send a GPS location pin to a contact or group.
| Name | Type | Required | Description |
|---|---|---|---|
| session | string | REQUIRED | Your session name |
| chatId | string | REQUIRED | Recipient chat ID |
| latitude | number | REQUIRED | Latitude (-90 to 90) |
| longitude | number | REQUIRED | Longitude (-180 to 180) |
| title | string | OPTIONAL | Location name |
curl -X POST https://proxy.whatsscale.com/api/sendLocation \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session": "YOUR_SESSION",
"chatId": "[email protected]",
"latitude": 48.8584,
"longitude": 2.2945,
"title": "Eiffel Tower"
}'{
"key": {
"remoteJid": "[email protected]",
"fromMe": true,
"id": "3EB0..."
},
"status": "PENDING"
}/api/sendPollSend Poll
Send an interactive poll. WhatsApp supports 2-12 options.
| Name | Type | Required | Description |
|---|---|---|---|
| session | string | REQUIRED | Your session name |
| chatId | string | REQUIRED | Recipient chat ID |
| question | string | REQUIRED | Poll question |
| options | string[] | REQUIRED | Array of 2-12 unique options |
| multipleAnswers | boolean | OPTIONAL | Allow multiple selections (default: false) |
curl -X POST https://proxy.whatsscale.com/api/sendPoll \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session": "YOUR_SESSION",
"chatId": "[email protected]",
"question": "What is your favorite color?",
"options": ["Red", "Blue", "Green"],
"multipleAnswers": false
}'{
"key": {
"remoteJid": "[email protected]",
"fromMe": true,
"id": "3EB0..."
},
"status": "PENDING"
}Options must be unique strings. Minimum 2, maximum 12.
/api/status/:jobIdCheck Job Status
Check the status of an async job (video sending, story posting).
| Name | Type | Required | Description |
|---|---|---|---|
| jobId | string | REQUIRED | Job ID returned from sendVideo or story endpoints |
curl -X GET https://proxy.whatsscale.com/api/status/YOUR_JOB_ID \
-H "X-Api-Key: YOUR_API_KEY"{
"jobId": "abc123-def456",
"status": "COMPLETED",
"createdAt": "2025-01-15T10:30:00Z"
}Possible statuses: QUEUED, PROCESSING, COMPLETED, FAILED.
Jobs expire after 1 hour.
WhatsApp Status (Stories)
Post text, image, and video stories to your WhatsApp status.
/api/status/textPost Text Story
Post a text-only status with a background color.
| Name | Type | Required | Description |
|---|---|---|---|
| session | string | REQUIRED | Your session name |
| text | string | REQUIRED | Status text |
| backgroundColor | string | OPTIONAL | Hex color (default: #25D366) |
curl -X POST https://proxy.whatsscale.com/api/status/text \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session": "YOUR_SESSION",
"text": "Big announcement coming soon!",
"backgroundColor": "#25D366"
}'{
"key": {
"remoteJid": "status@broadcast",
"fromMe": true,
"id": "3EB0..."
},
"status": "PENDING"
}/api/status/imagePost Image Story
Post an image to your WhatsApp status.
| Name | Type | Required | Description |
|---|---|---|---|
| session | string | REQUIRED | Your session name |
| file | string | REQUIRED | Public URL to the image |
| caption | string | OPTIONAL | Image caption |
curl -X POST https://proxy.whatsscale.com/api/status/image \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session": "YOUR_SESSION",
"file": "https://example.com/promo.jpg",
"caption": "New product launch!"
}'{
"key": {
"remoteJid": "status@broadcast",
"fromMe": true,
"id": "3EB0..."
},
"status": "PENDING"
}/api/status/videoPost Video Story
Post a video to your WhatsApp status. Processed asynchronously.
| Name | Type | Required | Description |
|---|---|---|---|
| session | string | REQUIRED | Your session name |
| file | string | REQUIRED | Public URL to the video |
| caption | string | OPTIONAL | Video caption |
curl -X POST https://proxy.whatsscale.com/api/status/video \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session": "YOUR_SESSION",
"file": "https://example.com/story.mp4",
"caption": "Behind the scenes"
}'{
"jobId": "abc123-def456",
"status": "QUEUED",
"message": "Video story queued for processing"
}Video stories are async. Use GET /api/status/:jobId to track.
Utility
Helper endpoints for phone number validation.
/api/checkWhatsappCheck WhatsApp Number
Check if a phone number is registered on WhatsApp before sending.
| Name | Type | Required | Description |
|---|---|---|---|
| session | string | REQUIRED | Your session name |
| phone | string | REQUIRED | Phone number to check (with or without +) |
curl -X POST https://proxy.whatsscale.com/api/checkWhatsapp \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session": "YOUR_SESSION",
"phone": "+31612345678"
}'{
"numberExists": true,
"phone": "+31612345678",
"phoneFormatted": "31612345678",
"chatId": "[email protected]"
}Phone numbers are automatically normalized (strips +, spaces, dashes).
Must be 10-15 digits after normalization.
CRM
Manage contacts with tags and metadata for targeted messaging.
/api/crm/contactsCreate Contact
Add a new contact to your CRM.
| Name | Type | Required | Description |
|---|---|---|---|
| phone | string | REQUIRED | Phone number |
| name | string | OPTIONAL | Contact name |
| tags | string | string[] | OPTIONAL | Tags — comma-separated string or array |
curl -X POST https://proxy.whatsscale.com/api/crm/contacts \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone": "+31612345678",
"name": "John Doe",
"tags": ["customer", "vip"]
}'{
"id": "uuid-here",
"phone": "+31612345678",
"name": "John Doe",
"tags": ["customer", "vip"],
"created_at": "2025-01-15T10:30:00Z"
}/api/crm/contactsList Contacts
Get a paginated list of your CRM contacts.
| Name | Type | Required | Description |
|---|---|---|---|
| page | number | OPTIONAL | Page number (default: 1) |
| limit | number | OPTIONAL | Results per page (default: 50) |
| tag | string | OPTIONAL | Filter by tag |
| search | string | OPTIONAL | Search by name or phone |
curl -X GET "https://proxy.whatsscale.com/api/crm/contacts?tag=vip&limit=10" \
-H "X-Api-Key: YOUR_API_KEY"[
{
"id": "uuid-here",
"phone": "+31612345678",
"name": "John Doe",
"tags": ["customer", "vip"]
}
]/api/crm/contacts/:idGet Contact
Get a single contact by ID.
| Name | Type | Required | Description |
|---|---|---|---|
| id | uuid | REQUIRED | Contact ID |
curl -X GET https://proxy.whatsscale.com/api/crm/contacts/CONTACT_ID \
-H "X-Api-Key: YOUR_API_KEY"{
"id": "uuid-here",
"phone": "+31612345678",
"name": "John Doe",
"tags": ["customer", "vip"],
"created_at": "2025-01-15T10:30:00Z"
}/api/crm/contacts/phone/:phoneFind Contact by Phone
Look up a contact by phone number. Returns 404 if not found.
| Name | Type | Required | Description |
|---|---|---|---|
| phone | string | REQUIRED | Phone number |
curl -X GET https://proxy.whatsscale.com/api/crm/contacts/phone/31612345678 \
-H "X-Api-Key: YOUR_API_KEY"{
"id": "uuid-here",
"phone": "+31612345678",
"name": "John Doe",
"tags": ["customer", "vip"]
}/api/crm/contacts/:idUpdate Contact
Update a contact's name or tags.
| Name | Type | Required | Description |
|---|---|---|---|
| id | uuid | REQUIRED | Contact ID |
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | OPTIONAL | Updated name |
| tags | string | string[] | OPTIONAL | Replace all tags |
curl -X PATCH https://proxy.whatsscale.com/api/crm/contacts/CONTACT_ID \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "John Smith",
"tags": ["customer", "premium"]
}'{
"id": "uuid-here",
"phone": "+31612345678",
"name": "John Smith",
"tags": ["customer", "premium"]
}/api/crm/contacts/:idDelete Contact
Permanently delete a contact from your CRM.
| Name | Type | Required | Description |
|---|---|---|---|
| id | uuid | REQUIRED | Contact ID |
curl -X DELETE https://proxy.whatsscale.com/api/crm/contacts/CONTACT_ID \
-H "X-Api-Key: YOUR_API_KEY"{
"message": "Contact deleted"
}/api/crm/contacts/:id/tagsAdd Tag to Contact
Add a single tag to an existing contact.
| Name | Type | Required | Description |
|---|---|---|---|
| id | uuid | REQUIRED | Contact ID |
| Name | Type | Required | Description |
|---|---|---|---|
| tag | string | REQUIRED | Tag to add |
curl -X POST https://proxy.whatsscale.com/api/crm/contacts/CONTACT_ID/tags \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"tag": "premium"}'{
"id": "uuid-here",
"tags": ["customer", "vip", "premium"]
}/api/crm/contacts/:id/tags/:tagRemove Tag from Contact
Remove a specific tag from a contact.
| Name | Type | Required | Description |
|---|---|---|---|
| id | uuid | REQUIRED | Contact ID |
| tag | string | REQUIRED | Tag to remove |
curl -X DELETE https://proxy.whatsscale.com/api/crm/contacts/CONTACT_ID/tags/premium \
-H "X-Api-Key: YOUR_API_KEY"{
"id": "uuid-here",
"tags": ["customer", "vip"]
}Public Webhook API
Self-serve webhook subscriptions for incoming WhatsApp messages. HMAC-SHA-256 signed deliveries, exponential-backoff retry, dead-letter visibility. See the full conceptual guide at /docs/webhooks.
/v1/webhooks/subscribeSubscribe a Webhook
Register a webhook URL for incoming messages. Returns the signing_secret ONCE — store it immediately. Plan-gated to Starter+. Duplicate (session + trigger_type + filter_id + webhook_url) returns 409 with the existing subscription_id. Cap of 50 active subscriptions per user; overflow returns 422.
| Name | Type | Required | Description |
|---|---|---|---|
| session | string | REQUIRED | Your session name (from /api/sessions) |
| webhook_url | string | REQUIRED | HTTPS URL to receive deliveries. Must not be localhost/private IP on production. |
| trigger_type | string | OPTIONAL | One of "1on1" (default), "group", "channel" |
| filter_id | string | OPTIONAL | Group/channel ID to scope to. NULL = all chats matching trigger_type. |
curl -X POST https://proxy.whatsscale.com/v1/webhooks/subscribe \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session": "YOUR_SESSION",
"webhook_url": "https://api.example.com/wh/incoming",
"trigger_type": "1on1"
}'{
"subscription_id": "550e8400-e29b-41d4-a716-446655440000",
"signing_secret": "whsec_abcd1234EFGH5678ijklMNOPqrst9012UVWXyz3456789012",
"webhook_url": "https://api.example.com/wh/incoming",
"trigger_type": "1on1",
"filter_id": null,
"created_at": "2026-04-30T12:00:00.000Z"
}signing_secret is shown ONCE. To rotate, DELETE this subscription and re-subscribe.
On HTTP 409 (duplicate), the response body contains the existing subscription_id; no new secret is generated.
On HTTP 422 (cap reached), delete unused subscriptions or contact support.
See /docs/webhooks for signature verification snippets and retry semantics.
/v1/webhooksList Your Webhook Subscriptions
Returns all webhook subscriptions belonging to your account. signing_secret is NEVER included in this response.
curl -X GET https://proxy.whatsscale.com/v1/webhooks \
-H "X-Api-Key: YOUR_API_KEY"{
"subscriptions": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"session": "YOUR_SESSION",
"webhook_url": "https://api.example.com/wh/incoming",
"trigger_type": "1on1",
"filter_id": null,
"is_active": true,
"failure_count": 0,
"last_triggered_at": "2026-04-30T12:00:00.000Z",
"last_success_at": "2026-04-30T12:00:00.500Z",
"last_error": null,
"created_at": "2026-04-29T10:00:00.000Z"
}
]
}/v1/webhooks/:idDelete a Webhook Subscription
Removes a subscription by ID. Idempotent — returns success whether the row existed or not. Scoped to your account.
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | REQUIRED | Subscription UUID from the subscribe response or list endpoint |
curl -X DELETE https://proxy.whatsscale.com/v1/webhooks/550e8400-e29b-41d4-a716-446655440000 \
-H "X-Api-Key: YOUR_API_KEY"{ "success": true }/v1/webhooks/:id/failuresList Failed Deliveries (DLQ)
Returns recent dead-letter deliveries for a subscription. Pagination via ?limit=N (default 50, max 200) and ?before=<ISO timestamp>. Returns 404 for any subscription you do not own.
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | REQUIRED | Subscription UUID |
curl -X GET "https://proxy.whatsscale.com/v1/webhooks/550e8400-e29b-41d4-a716-446655440000/failures?limit=20" \
-H "X-Api-Key: YOUR_API_KEY"{
"deliveries": [
{
"id": "abc12345-...",
"status": "dead",
"attempt_count": 6,
"last_status_code": 410,
"last_error": "HTTP 410",
"last_attempt_at": "2026-04-30T11:45:00.000Z",
"payload_preview": "{\"event_id\":\"...\",\"event_type\":\"incoming.message\",\"trigger_type\":\"1on1\",\"data\":{...}}"
}
],
"has_more": false
}A delivery is marked dead after 6 failed attempts (≈7.5 hours total) OR immediately on a permanent 4xx (anything except 408 and 429).
payload_preview is the first 500 chars of the JSON-stringified envelope. It is opaque text, not parseable JSON — cuts mid-string or mid-object are likely.
For full delivery history (any status, not just dead), see GET /v1/webhooks/:id/deliveries below. To replay a dead delivery, see POST /v1/webhooks/deliveries/:id/retry.
/v1/webhooks/:id/deliveriesList Deliveries (Any Status)
Returns recent deliveries for a subscription across all statuses (pending, delivering, succeeded, failed, dead). Use this to confirm an event arrived (?status=succeeded) or trace what happened to a specific event_id. Pagination uses a cursor PAIR: pass back the response's next_cursor as ?before_ts=&before_id= on the next call. The pair shape is necessary because under burst load multiple delivery rows can share a microsecond timestamp; a timestamp-only cursor would silently skip rows. Returns 404 for any subscription you do not own.
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | REQUIRED | Subscription UUID |
curl -X GET "https://proxy.whatsscale.com/v1/webhooks/550e8400-e29b-41d4-a716-446655440000/deliveries?limit=20&status=succeeded" \
-H "X-Api-Key: YOUR_API_KEY"{
"deliveries": [
{
"id": "abc12345-...",
"event_id": "550e8400-...",
"status": "succeeded",
"attempt_count": 1,
"last_status_code": 200,
"last_error": null,
"last_attempt_at": "2026-05-04T16:17:58.202Z",
"created_at": "2026-05-04T16:17:57.899Z",
"payload_preview": "{\"data\":{\"body\":\"...\",\"chat_id\":\"...\"}}"
}
],
"has_more": true,
"next_cursor": {
"before_ts": "2026-05-04T16:17:57.899Z",
"before_id": "abc12345-..."
}
}Both before_ts and before_id are required together when paginating — pass back the next_cursor object verbatim. Sending only one half returns 400.
The status filter accepts pending, delivering, succeeded, failed, or dead (case-sensitive). Omit it to see all statuses.
next_cursor is null when has_more is false — you have reached the end of history.
/v1/webhooks/deliveries/:id/retryManually Replay a Dead Delivery
Re-enqueues a delivery that previously transitioned to "dead" — for example, after you fixed your endpoint following an outage. Inserts a NEW delivery row linked to the original via parent_delivery_id; the original "dead" row is preserved unchanged. The replay carries the SAME event_id as the original (Stripe-style — your dedupe layer should treat it as the same logical event). Rate limit: 30/min/user.
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | REQUIRED | Delivery row UUID (NOT subscription UUID) — typically obtained from a prior GET /deliveries?status=dead call |
curl -X POST https://proxy.whatsscale.com/v1/webhooks/deliveries/abc12345-e29b-41d4-a716-446655440000/retry \
-H "X-Api-Key: YOUR_API_KEY"{
"success": true,
"new_delivery_id": "def67890-...",
"parent_delivery_id": "abc12345-...",
"event_id": "550e8400-..."
}Only deliveries in "dead" state are retryable. Non-dead returns 409 with current_status in the body.
409 with "Replay already in flight" + existing_retry_id means a previous retry on this same parent is still pending/delivering/failed. Wait for it to terminate before retrying again.
410 Gone means the original subscription was deleted — replay is no longer possible.
CRITICAL — retry storm prevention: when your endpoint detects a duplicate event_id (replay of an event you already processed) and chooses to discard, return HTTP 200 OK with an empty body. Never return 4xx for "already processed" — a 4xx tells WhatsScale the delivery failed and triggers another replay. 200 OK with no work done is the correct dedupe response.
Group Management
List participants, add/remove members, promote/demote admins, and leave groups. Designed to pair with incoming-message webhook events: receive a group webhook, decide a participant is spam, call /participants/remove with the same data.participant_id. Plan-gated to Starter+. Session ownership enforced — the calling API key must own the session.
/v1/groups/:groupId/participantsList Group Participants
Returns the participant list for a group: each participant's LID (id), phone form (pn — best-effort, may be empty per WhatsApp privacy mode), and role. The id round-trips with the participant_id you receive in group webhook events. Pagination via ?limit (default 100, max 500) and ?offset (default 0) — the proxy slices server-side.
| Name | Type | Required | Description |
|---|---|---|---|
| groupId | string | REQUIRED | Group JID, ending @g.us. Example: [email protected] |
curl -X GET "https://proxy.whatsscale.com/v1/groups/[email protected]/participants?session=user_xxx&limit=50" \
-H "X-Api-Key: YOUR_API_KEY"[
{
"id": "263195814002890@lid",
"pn": "[email protected]",
"role": "participant"
},
{
"id": "215659065667785@lid",
"pn": "[email protected]",
"role": "superadmin"
}
]role is one of "participant", "admin", or "superadmin". Most engines require superadmin (group creator) to promote/demote others.
pn is best-effort. WhatsApp may omit it depending on the participant's privacy settings or whether the session has them saved as a contact. Always handle empty-string defensively.
A non-existent groupId returns 502 with code: 500 — that's the underlying engine's response. Treat it as "fix your groupId, do not retry."
/v1/groups/:groupId/participants/addAdd Participants
Adds one or more participants to a group. Body: { session, participants: [{ id }] }. Each id is either <digits>@c.us (phone form) or <digits>@lid (LID form); both accepted in the same array. Max 50 entries per call. Rate limit: 30/min.
| Name | Type | Required | Description |
|---|---|---|---|
| groupId | string | REQUIRED | Group JID, ending @g.us |
| Name | Type | Required | Description |
|---|---|---|---|
| session | string | REQUIRED | Session name owned by the calling API key |
| participants | array | REQUIRED | Array of { id: "<jid>" } objects. Bare-string format ["<jid>"] is REJECTED with 400 — that format crashes the engine. |
curl -X POST https://proxy.whatsscale.com/v1/groups/[email protected]/participants/add \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session": "user_xxx",
"participants": [
{ "id": "[email protected]" }
]
}'// engine-specific shape — passed through verbatim from upstreamWhen a previously-removed participant rejoins via this endpoint, their LID for the group may regenerate. Cached LIDs from before the kick are not guaranteed to match. Re-list participants if you need the current LID.
Adding an already-in-group participant returns 4xx — treat as success-equivalent in your retry logic.
/v1/groups/:groupId/participants/removeRemove Participants (kick)
PRIMARY MODERATION ACTION. Kicks one or more participants. Same body shape as /add. When you receive a group webhook event and identify spam, pass the data.participant_id from the event back here directly — no translation needed.
| Name | Type | Required | Description |
|---|---|---|---|
| groupId | string | REQUIRED | Group JID, ending @g.us |
| Name | Type | Required | Description |
|---|---|---|---|
| session | string | REQUIRED | Session name |
| participants | array | REQUIRED | Array of { id: "<jid>" } — typically the participant_id from a webhook event |
curl -X POST https://proxy.whatsscale.com/v1/groups/[email protected]/participants/remove \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session": "user_xxx",
"participants": [
{ "id": "263195814002890@lid" }
]
}'// engine-specific shape — passed through verbatim from upstreamRemoving an already-not-in-group participant returns 4xx — treat as success-equivalent.
After a kick, if the participant rejoins later their LID may regenerate. Don't cache participant LIDs across rejoin events.
/v1/groups/:groupId/admin/promotePromote to Admin
Grants admin role to one or more participants. Same body shape as /add. The calling session must be superadmin (group creator) on most engines — admin role alone is usually insufficient.
| Name | Type | Required | Description |
|---|---|---|---|
| groupId | string | REQUIRED | Group JID, ending @g.us |
| Name | Type | Required | Description |
|---|---|---|---|
| session | string | REQUIRED | Session name |
| participants | array | REQUIRED | Array of { id: "<jid>" } |
curl -X POST https://proxy.whatsscale.com/v1/groups/[email protected]/admin/promote \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "session": "user_xxx", "participants": [{ "id": "263195814002890@lid" }] }'// engine-specific shape — passed through verbatim from upstreamPromoting a participant who is already admin returns 4xx — treat as success-equivalent.
/v1/groups/:groupId/admin/demoteDemote from Admin
Revokes admin role. Same body shape as /promote. Useful for auto-demoting spammer-admins after detecting abuse via webhook events.
| Name | Type | Required | Description |
|---|---|---|---|
| groupId | string | REQUIRED | Group JID, ending @g.us |
| Name | Type | Required | Description |
|---|---|---|---|
| session | string | REQUIRED | Session name |
| participants | array | REQUIRED | Array of { id: "<jid>" } |
curl -X POST https://proxy.whatsscale.com/v1/groups/[email protected]/admin/demote \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "session": "user_xxx", "participants": [{ "id": "263195814002890@lid" }] }'// engine-specific shape — passed through verbatim from upstreamDemoting a non-admin returns 4xx — treat as success-equivalent.
/v1/groups/:groupId/leaveLeave a Group
The session leaves the specified group. Body is session-only — no participants array. IRREVERSIBLE: once you leave, rejoining requires a new invite link.
| Name | Type | Required | Description |
|---|---|---|---|
| groupId | string | REQUIRED | Group JID, ending @g.us |
| Name | Type | Required | Description |
|---|---|---|---|
| session | string | REQUIRED | Session name owned by the calling API key |
curl -X POST https://proxy.whatsscale.com/v1/groups/[email protected]/leave \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "session": "user_xxx" }'// engine-specific shape — passed through verbatim from upstreamUse sparingly — there is no undo. To rejoin, you need a fresh invite link from someone still in the group.
Chat ID Format
| Type | Format | Example |
|---|---|---|
| Individual | [email protected] | [email protected] |
| Group | [email protected] | [email protected] |
| Channel | id@newsletter | 120363012345678@newsletter |
Phone numbers should not include + or spaces. Example: 31612345678, not +316 1234 5678.
Rate Limits
| Scope | Limit |
|---|---|
| Global (all endpoints) | 100 requests / minute |
| Send endpoints | 30 requests / minute |
| Authentication | 10 requests / minute |
Rate-limited requests return HTTP 429. Wait and retry.
Error Codes
| Code | Meaning |
|---|---|
| 200 | Success |
| 400 | Bad request — missing or invalid parameters |
| 401 | Unauthorized — invalid or missing API key |
| 403 | Forbidden — session belongs to another user |
| 404 | Not found — session, contact, or job not found |
| 429 | Rate limited — too many requests |
| 500 | Server error — try again or contact support |
Questions? Contact [email protected]