API Reference

Complete reference for the WhatsScale API. Send messages, manage contacts, post stories, and automate your WhatsApp.

New: Want to test endpoints interactively? Try our API Explorer (Swagger UI) →

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.

GET/api/auth/test

Test 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"
Response
{
  "success": true,
  "user": {
    "id": "user-uuid",
    "email": "[email protected]"
  }
}
GET/api/sessions

List 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"
Response
[
  {
    "name": "user_example0000000000000000000000_Ab12Cd34",
    "status": "WORKING",
    "me": {
      "id": "[email protected]",
      "pushName": "John"
    }
  }
]
Notes

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.

GET/api/:session/contacts

List WhatsApp Contacts

Get all contacts synced from your WhatsApp.

Path Parameters
NameTypeRequiredDescription
sessionstringREQUIREDSession name from /api/sessions
curl -X GET https://proxy.whatsscale.com/api/YOUR_SESSION/contacts \
  -H "X-Api-Key: YOUR_API_KEY"
Response
[
  {
    "id": "[email protected]",
    "name": "John Doe",
    "pushName": "John"
  }
]
Notes

Contact ID format: phone number + "@c.us" (e.g. [email protected]).

GET/api/:session/groups

List WhatsApp Groups

Get all groups you are a member of.

Path Parameters
NameTypeRequiredDescription
sessionstringREQUIREDSession name from /api/sessions
curl -X GET https://proxy.whatsscale.com/api/YOUR_SESSION/groups \
  -H "X-Api-Key: YOUR_API_KEY"
Response
[
  {
    "id": "[email protected]",
    "subject": "Team Chat",
    "size": 12
  }
]
Notes

Group ID format: number + "@g.us".

Messaging

Send text, images, videos, documents, locations, and polls.

POST/api/sendText

Send Text Message

Send a plain text message to a contact, group, or channel.

Request Body (JSON)
NameTypeRequiredDescription
sessionstringREQUIREDYour session name
chatIdstringREQUIREDRecipient: [email protected] or [email protected]
textstringREQUIREDMessage 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!"
  }'
Response
{
  "key": {
    "remoteJid": "[email protected]",
    "fromMe": true,
    "id": "3EB0..."
  },
  "status": "PENDING"
}
POST/api/sendImage

Send Image

Send an image via URL with an optional caption.

Request Body (JSON)
NameTypeRequiredDescription
sessionstringREQUIREDYour session name
chatIdstringREQUIREDRecipient chat ID
filestringREQUIREDPublic URL to the image
captionstringOPTIONALImage 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!"
  }'
Response
{
  "key": {
    "remoteJid": "[email protected]",
    "fromMe": true,
    "id": "3EB0..."
  },
  "status": "PENDING"
}
POST/api/sendVideo

Send Video

Send a video via URL. Videos are processed asynchronously.

Request Body (JSON)
NameTypeRequiredDescription
sessionstringREQUIREDYour session name
chatIdstringREQUIREDRecipient chat ID
filestringREQUIREDPublic URL to the video
captionstringOPTIONALVideo 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!"
  }'
Response
{
  "jobId": "abc123-def456",
  "status": "QUEUED",
  "message": "Video queued for processing"
}
Notes

Video sending is async. Poll GET /api/status/:jobId to check progress.

POST/api/sendDocument

Send Document

Send a document (PDF, DOCX, etc.) via URL.

Request Body (JSON)
NameTypeRequiredDescription
sessionstringREQUIREDYour session name
chatIdstringREQUIREDRecipient chat ID
filestringREQUIREDPublic URL to the document
filenamestringOPTIONALDisplay filename (auto-detected from URL if omitted)
captionstringOPTIONALDocument 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"
  }'
Response
{
  "key": {
    "remoteJid": "[email protected]",
    "fromMe": true,
    "id": "3EB0..."
  },
  "status": "PENDING"
}
POST/api/sendAudio

Send 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.

Request Body (JSON)
NameTypeRequiredDescription
sessionstringREQUIREDYour session name
chatIdstringREQUIREDRecipient — contact (@c.us) or group (@g.us)
filestringREQUIREDPublic URL to the audio file (mp3, m4a, aac, ogg, wav)
voicebooleanOPTIONALtrue (default) sends a native voice note, transcoded to Opus automatically. false sends the audio as a file attachment.
filenamestringOPTIONALDisplay filename (auto-detected from URL if omitted). Ignored for voice notes.
captionstringOPTIONALCaption (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"
  }'
Response
{
  "key": {
    "remoteJid": "[email protected]",
    "fromMe": true,
    "id": "3EB0..."
  },
  "status": "PENDING"
}
Notes

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.

POST/api/sendLocation

Send Location

Send a GPS location pin to a contact or group.

Request Body (JSON)
NameTypeRequiredDescription
sessionstringREQUIREDYour session name
chatIdstringREQUIREDRecipient chat ID
latitudenumberREQUIREDLatitude (-90 to 90)
longitudenumberREQUIREDLongitude (-180 to 180)
titlestringOPTIONALLocation 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"
  }'
Response
{
  "key": {
    "remoteJid": "[email protected]",
    "fromMe": true,
    "id": "3EB0..."
  },
  "status": "PENDING"
}
POST/api/sendPoll

Send Poll

Send an interactive poll. WhatsApp supports 2-12 options.

Request Body (JSON)
NameTypeRequiredDescription
sessionstringREQUIREDYour session name
chatIdstringREQUIREDRecipient chat ID
questionstringREQUIREDPoll question
optionsstring[]REQUIREDArray of 2-12 unique options
multipleAnswersbooleanOPTIONALAllow 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
  }'
Response
{
  "key": {
    "remoteJid": "[email protected]",
    "fromMe": true,
    "id": "3EB0..."
  },
  "status": "PENDING"
}
Notes

Options must be unique strings. Minimum 2, maximum 12.

GET/api/status/:jobId

Check Job Status

Check the status of an async job (video sending, story posting).

Path Parameters
NameTypeRequiredDescription
jobIdstringREQUIREDJob 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"
Response
{
  "jobId": "abc123-def456",
  "status": "COMPLETED",
  "createdAt": "2025-01-15T10:30:00Z"
}
Notes

Possible statuses: QUEUED, PROCESSING, COMPLETED, FAILED.

Jobs expire after 1 hour.

WhatsApp Status (Stories)

Post text, image, and video stories to your WhatsApp status.

POST/api/status/text

Post Text Story

Post a text-only status with a background color.

Request Body (JSON)
NameTypeRequiredDescription
sessionstringREQUIREDYour session name
textstringREQUIREDStatus text
backgroundColorstringOPTIONALHex 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"
  }'
Response
{
  "key": {
    "remoteJid": "status@broadcast",
    "fromMe": true,
    "id": "3EB0..."
  },
  "status": "PENDING"
}
POST/api/status/image

Post Image Story

Post an image to your WhatsApp status.

Request Body (JSON)
NameTypeRequiredDescription
sessionstringREQUIREDYour session name
filestringREQUIREDPublic URL to the image
captionstringOPTIONALImage 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!"
  }'
Response
{
  "key": {
    "remoteJid": "status@broadcast",
    "fromMe": true,
    "id": "3EB0..."
  },
  "status": "PENDING"
}
POST/api/status/video

Post Video Story

Post a video to your WhatsApp status. Processed asynchronously.

Request Body (JSON)
NameTypeRequiredDescription
sessionstringREQUIREDYour session name
filestringREQUIREDPublic URL to the video
captionstringOPTIONALVideo 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"
  }'
Response
{
  "jobId": "abc123-def456",
  "status": "QUEUED",
  "message": "Video story queued for processing"
}
Notes

Video stories are async. Use GET /api/status/:jobId to track.

Utility

Helper endpoints for phone number validation.

POST/api/checkWhatsapp

Check WhatsApp Number

Check if a phone number is registered on WhatsApp before sending.

Request Body (JSON)
NameTypeRequiredDescription
sessionstringREQUIREDYour session name
phonestringREQUIREDPhone 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"
  }'
Response
{
  "numberExists": true,
  "phone": "+31612345678",
  "phoneFormatted": "31612345678",
  "chatId": "[email protected]"
}
Notes

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.

POST/api/crm/contacts

Create Contact

Add a new contact to your CRM.

Request Body (JSON)
NameTypeRequiredDescription
phonestringREQUIREDPhone number
namestringOPTIONALContact name
tagsstring | string[]OPTIONALTags — 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"]
  }'
Response
{
  "id": "uuid-here",
  "phone": "+31612345678",
  "name": "John Doe",
  "tags": ["customer", "vip"],
  "created_at": "2025-01-15T10:30:00Z"
}
GET/api/crm/contacts

List Contacts

Get a paginated list of your CRM contacts.

Query Parameters
NameTypeRequiredDescription
pagenumberOPTIONALPage number (default: 1)
limitnumberOPTIONALResults per page (default: 50)
tagstringOPTIONALFilter by tag
searchstringOPTIONALSearch 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"
Response
[
  {
    "id": "uuid-here",
    "phone": "+31612345678",
    "name": "John Doe",
    "tags": ["customer", "vip"]
  }
]
GET/api/crm/contacts/:id

Get Contact

Get a single contact by ID.

Path Parameters
NameTypeRequiredDescription
iduuidREQUIREDContact ID
curl -X GET https://proxy.whatsscale.com/api/crm/contacts/CONTACT_ID \
  -H "X-Api-Key: YOUR_API_KEY"
Response
{
  "id": "uuid-here",
  "phone": "+31612345678",
  "name": "John Doe",
  "tags": ["customer", "vip"],
  "created_at": "2025-01-15T10:30:00Z"
}
GET/api/crm/contacts/phone/:phone

Find Contact by Phone

Look up a contact by phone number. Returns 404 if not found.

Path Parameters
NameTypeRequiredDescription
phonestringREQUIREDPhone number
curl -X GET https://proxy.whatsscale.com/api/crm/contacts/phone/31612345678 \
  -H "X-Api-Key: YOUR_API_KEY"
Response
{
  "id": "uuid-here",
  "phone": "+31612345678",
  "name": "John Doe",
  "tags": ["customer", "vip"]
}
PATCH/api/crm/contacts/:id

Update Contact

Update a contact's name or tags.

Path Parameters
NameTypeRequiredDescription
iduuidREQUIREDContact ID
Request Body (JSON)
NameTypeRequiredDescription
namestringOPTIONALUpdated name
tagsstring | string[]OPTIONALReplace 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"]
  }'
Response
{
  "id": "uuid-here",
  "phone": "+31612345678",
  "name": "John Smith",
  "tags": ["customer", "premium"]
}
DELETE/api/crm/contacts/:id

Delete Contact

Permanently delete a contact from your CRM.

Path Parameters
NameTypeRequiredDescription
iduuidREQUIREDContact ID
curl -X DELETE https://proxy.whatsscale.com/api/crm/contacts/CONTACT_ID \
  -H "X-Api-Key: YOUR_API_KEY"
Response
{
  "message": "Contact deleted"
}
POST/api/crm/contacts/:id/tags

Add Tag to Contact

Add a single tag to an existing contact.

Path Parameters
NameTypeRequiredDescription
iduuidREQUIREDContact ID
Request Body (JSON)
NameTypeRequiredDescription
tagstringREQUIREDTag 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"}'
Response
{
  "id": "uuid-here",
  "tags": ["customer", "vip", "premium"]
}
DELETE/api/crm/contacts/:id/tags/:tag

Remove Tag from Contact

Remove a specific tag from a contact.

Path Parameters
NameTypeRequiredDescription
iduuidREQUIREDContact ID
tagstringREQUIREDTag to remove
curl -X DELETE https://proxy.whatsscale.com/api/crm/contacts/CONTACT_ID/tags/premium \
  -H "X-Api-Key: YOUR_API_KEY"
Response
{
  "id": "uuid-here",
  "tags": ["customer", "vip"]
}
GET/api/crm/tags

List All Tags

Get all unique tags used across your CRM contacts.

curl -X GET https://proxy.whatsscale.com/api/crm/tags \
  -H "X-Api-Key: YOUR_API_KEY"
Response
["customer", "vip", "premium", "lead"]

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.

POST/v1/webhooks/subscribe

Subscribe 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.

Request Body (JSON)
NameTypeRequiredDescription
sessionstringREQUIREDYour session name (from /api/sessions)
webhook_urlstringREQUIREDHTTPS URL to receive deliveries. Must not be localhost/private IP on production.
trigger_typestringOPTIONALOne of "1on1" (default), "group", "channel"
filter_idstringOPTIONALGroup/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"
  }'
Response
{
  "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"
}
Notes

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.

GET/v1/webhooks

List 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"
Response
{
  "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"
    }
  ]
}
DELETE/v1/webhooks/:id

Delete a Webhook Subscription

Removes a subscription by ID. Idempotent — returns success whether the row existed or not. Scoped to your account.

Path Parameters
NameTypeRequiredDescription
idstringREQUIREDSubscription 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"
Response
{ "success": true }
GET/v1/webhooks/:id/failures

List 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.

Path Parameters
NameTypeRequiredDescription
idstringREQUIREDSubscription 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"
Response
{
  "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
}
Notes

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.

GET/v1/webhooks/:id/deliveries

List 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.

Path Parameters
NameTypeRequiredDescription
idstringREQUIREDSubscription 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"
Response
{
  "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-..."
  }
}
Notes

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.

POST/v1/webhooks/deliveries/:id/retry

Manually 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.

Path Parameters
NameTypeRequiredDescription
idstringREQUIREDDelivery 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"
Response
{
  "success": true,
  "new_delivery_id": "def67890-...",
  "parent_delivery_id": "abc12345-...",
  "event_id": "550e8400-..."
}
Notes

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.

GET/v1/groups/:groupId/participants

List 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.

Path Parameters
NameTypeRequiredDescription
groupIdstringREQUIREDGroup 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"
Response
[
  {
    "id": "263195814002890@lid",
    "pn": "[email protected]",
    "role": "participant"
  },
  {
    "id": "215659065667785@lid",
    "pn": "[email protected]",
    "role": "superadmin"
  }
]
Notes

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."

POST/v1/groups/:groupId/participants/add

Add 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.

Path Parameters
NameTypeRequiredDescription
groupIdstringREQUIREDGroup JID, ending @g.us
Request Body (JSON)
NameTypeRequiredDescription
sessionstringREQUIREDSession name owned by the calling API key
participantsarrayREQUIREDArray 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]" }
    ]
  }'
Response
// engine-specific shape — passed through verbatim from upstream
Notes

When 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.

POST/v1/groups/:groupId/participants/remove

Remove 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.

Path Parameters
NameTypeRequiredDescription
groupIdstringREQUIREDGroup JID, ending @g.us
Request Body (JSON)
NameTypeRequiredDescription
sessionstringREQUIREDSession name
participantsarrayREQUIREDArray 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" }
    ]
  }'
Response
// engine-specific shape — passed through verbatim from upstream
Notes

Removing 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.

POST/v1/groups/:groupId/admin/promote

Promote 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.

Path Parameters
NameTypeRequiredDescription
groupIdstringREQUIREDGroup JID, ending @g.us
Request Body (JSON)
NameTypeRequiredDescription
sessionstringREQUIREDSession name
participantsarrayREQUIREDArray 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" }] }'
Response
// engine-specific shape — passed through verbatim from upstream
Notes

Promoting a participant who is already admin returns 4xx — treat as success-equivalent.

POST/v1/groups/:groupId/admin/demote

Demote from Admin

Revokes admin role. Same body shape as /promote. Useful for auto-demoting spammer-admins after detecting abuse via webhook events.

Path Parameters
NameTypeRequiredDescription
groupIdstringREQUIREDGroup JID, ending @g.us
Request Body (JSON)
NameTypeRequiredDescription
sessionstringREQUIREDSession name
participantsarrayREQUIREDArray 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" }] }'
Response
// engine-specific shape — passed through verbatim from upstream
Notes

Demoting a non-admin returns 4xx — treat as success-equivalent.

POST/v1/groups/:groupId/leave

Leave 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.

Path Parameters
NameTypeRequiredDescription
groupIdstringREQUIREDGroup JID, ending @g.us
Request Body (JSON)
NameTypeRequiredDescription
sessionstringREQUIREDSession 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" }'
Response
// engine-specific shape — passed through verbatim from upstream
Notes

Use sparingly — there is no undo. To rejoin, you need a fresh invite link from someone still in the group.

Chat ID Format

TypeFormatExample
Individual[email protected][email protected]
Group[email protected][email protected]
Channelid@newsletter120363012345678@newsletter

Phone numbers should not include + or spaces. Example: 31612345678, not +316 1234 5678.

Rate Limits

ScopeLimit
Global (all endpoints)100 requests / minute
Send endpoints30 requests / minute
Authentication10 requests / minute

Rate-limited requests return HTTP 429. Wait and retry.

Error Codes

CodeMeaning
200Success
400Bad request — missing or invalid parameters
401Unauthorized — invalid or missing API key
403Forbidden — session belongs to another user
404Not found — session, contact, or job not found
429Rate limited — too many requests
500Server error — try again or contact support

Questions? Contact [email protected]