Send Messages
Sending all 14 message types via the WaSphere API — with cURL and Node.js examples for each.
Send Messages
WaSphere supports 14 WhatsApp message types. All message sending goes through the Dashboard API using this URL pattern:
POST https://api.yourdomain.com/workspaces/{workspaceId}/proxy/api/sessions/{sessionId}/messages/{type}
workspaceId— UUID found in your dashboard URL (e.g.be11c51b-1a5a-4bc9-9cb6-7ee7040ec4d8)sessionId— the session slug you set when creating the session (e.g.my-session)type— one of:text,image,video,audio,document,gif,sticker,location,contact,poll,buttons,list,view-once,reaction
Every request requires:
Authorization: Bearer wsk_live_xxxxxheaderContent-Type: application/jsonto— recipient phone number in international format without the+sign
Phone numbers must be in international format without + or spaces. Pakistan: 923001234567. UK: 447700900123. US: 12125550100.
Response format — every successful send returns:
{ "messageId": "3EB0C767D097B7C7A5C1", "status": "sent" }
1. Text
The simplest message type. Supports WhatsApp formatting: *bold*, _italic_, ~strikethrough~, `monospace`.
curl -X POST "https://api.yourdomain.com/workspaces/be11c51b-1a5a-4bc9-9cb6-7ee7040ec4d8/proxy/api/sessions/my-session/messages/text" \
-H "Authorization: Bearer wsk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"to": "923001234567",
"text": "Hello! Your order *#1234* has been dispatched. Expected delivery: _Friday 28 May_."
}'const workspaceId = 'be11c51b-1a5a-4bc9-9cb6-7ee7040ec4d8';
const sessionId = 'my-session';
const response = await fetch(
`https://api.yourdomain.com/workspaces/${workspaceId}/proxy/api/sessions/${sessionId}/messages/text`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer wsk_live_xxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
to: '923001234567',
text: 'Hello! Your order *#1234* has been dispatched.',
}),
}
);
const { messageId, status } = await response.json();2. Image
Send an image with an optional caption. Accepts a public HTTPS URL.
curl -X POST "https://api.yourdomain.com/workspaces/be11c51b-1a5a-4bc9-9cb6-7ee7040ec4d8/proxy/api/sessions/my-session/messages/image" \
-H "Authorization: Bearer wsk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"to": "923001234567",
"url": "https://yourcdn.com/images/product-photo.jpg",
"caption": "Here is your custom product!"
}'await fetch(
`https://api.yourdomain.com/workspaces/${workspaceId}/proxy/api/sessions/${sessionId}/messages/image`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer wsk_live_xxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
to: '923001234567',
url: 'https://yourcdn.com/images/product-photo.jpg',
caption: 'Here is your custom product!',
}),
}
);3. Video
curl -X POST "https://api.yourdomain.com/workspaces/be11c51b-1a5a-4bc9-9cb6-7ee7040ec4d8/proxy/api/sessions/my-session/messages/video" \
-H "Authorization: Bearer wsk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"to": "923001234567",
"url": "https://yourcdn.com/videos/tutorial.mp4",
"caption": "Watch this quick setup guide"
}'await fetch(
`https://api.yourdomain.com/workspaces/${workspaceId}/proxy/api/sessions/${sessionId}/messages/video`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer wsk_live_xxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
to: '923001234567',
url: 'https://yourcdn.com/videos/tutorial.mp4',
caption: 'Watch this quick setup guide',
}),
}
);4. Audio
Send a voice note or an audio file attachment. Set isVoiceNote: true to render the inline waveform UI.
curl -X POST "https://api.yourdomain.com/workspaces/be11c51b-1a5a-4bc9-9cb6-7ee7040ec4d8/proxy/api/sessions/my-session/messages/audio" \
-H "Authorization: Bearer wsk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"to": "923001234567",
"url": "https://yourcdn.com/audio/greeting.ogg",
"isVoiceNote": false
}'await fetch(
`https://api.yourdomain.com/workspaces/${workspaceId}/proxy/api/sessions/${sessionId}/messages/audio`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer wsk_live_xxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
to: '923001234567',
url: 'https://yourcdn.com/audio/greeting.ogg',
isVoiceNote: false, // true = voice note waveform, false = audio file attachment
}),
}
);5. Document
Send any file as a document attachment. fileName controls what name the recipient sees.
The field name is fileName (camelCase), not filename. Using the wrong casing will result in a validation error.
curl -X POST "https://api.yourdomain.com/workspaces/be11c51b-1a5a-4bc9-9cb6-7ee7040ec4d8/proxy/api/sessions/my-session/messages/document" \
-H "Authorization: Bearer wsk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"to": "923001234567",
"url": "https://yourcdn.com/files/invoice-1234.pdf",
"fileName": "report.pdf",
"mimetype": "application/pdf"
}'await fetch(
`https://api.yourdomain.com/workspaces/${workspaceId}/proxy/api/sessions/${sessionId}/messages/document`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer wsk_live_xxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
to: '923001234567',
url: 'https://yourcdn.com/files/invoice-1234.pdf',
fileName: 'report.pdf',
mimetype: 'application/pdf',
}),
}
);6. GIF
Send an MP4 URL — WhatsApp renders it as an auto-playing GIF. The gif type is distinct from video because WhatsApp loops it silently without playback controls.
curl -X POST "https://api.yourdomain.com/workspaces/be11c51b-1a5a-4bc9-9cb6-7ee7040ec4d8/proxy/api/sessions/my-session/messages/gif" \
-H "Authorization: Bearer wsk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"to": "923001234567",
"url": "https://yourcdn.com/animations/celebration.mp4",
"caption": "Congratulations!"
}'await fetch(
`https://api.yourdomain.com/workspaces/${workspaceId}/proxy/api/sessions/${sessionId}/messages/gif`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer wsk_live_xxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
to: '923001234567',
url: 'https://yourcdn.com/animations/celebration.mp4',
caption: 'Congratulations!',
}),
}
);7. Sticker
Send a PNG or JPG image URL — WaSphere automatically converts it to WebP before delivery.
curl -X POST "https://api.yourdomain.com/workspaces/be11c51b-1a5a-4bc9-9cb6-7ee7040ec4d8/proxy/api/sessions/my-session/messages/sticker" \
-H "Authorization: Bearer wsk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"to": "923001234567",
"url": "https://yourcdn.com/stickers/thumbs-up.png"
}'await fetch(
`https://api.yourdomain.com/workspaces/${workspaceId}/proxy/api/sessions/${sessionId}/messages/sticker`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer wsk_live_xxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
to: '923001234567',
url: 'https://yourcdn.com/stickers/thumbs-up.png', // PNG or JPG — auto-converted to WebP
}),
}
);8. Location
Share a geographic location pin. The recipient sees a map thumbnail they can tap to open in their maps app.
curl -X POST "https://api.yourdomain.com/workspaces/be11c51b-1a5a-4bc9-9cb6-7ee7040ec4d8/proxy/api/sessions/my-session/messages/location" \
-H "Authorization: Bearer wsk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"to": "923001234567",
"latitude": 33.6844,
"longitude": 73.0479,
"name": "Islamabad",
"address": "Capital Territory, Pakistan"
}'await fetch(
`https://api.yourdomain.com/workspaces/${workspaceId}/proxy/api/sessions/${sessionId}/messages/location`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer wsk_live_xxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
to: '923001234567',
latitude: 33.6844,
longitude: 73.0479,
name: 'Islamabad',
address: 'Capital Territory, Pakistan',
}),
}
);9. Contact Card
Share a contact card. Use displayName and phoneNumber — not name or phone.
The fields are displayName and phoneNumber. The phone number in the contact card body accepts the + prefix (it is the vCard number, not the routing to field).
curl -X POST "https://api.yourdomain.com/workspaces/be11c51b-1a5a-4bc9-9cb6-7ee7040ec4d8/proxy/api/sessions/my-session/messages/contact" \
-H "Authorization: Bearer wsk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"to": "923001234567",
"displayName": "John Doe",
"phoneNumber": "+923001234567"
}'await fetch(
`https://api.yourdomain.com/workspaces/${workspaceId}/proxy/api/sessions/${sessionId}/messages/contact`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer wsk_live_xxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
to: '923001234567',
displayName: 'John Doe',
phoneNumber: '+923001234567',
}),
}
);10. Poll
Create an interactive poll. Recipients can vote and see live results. selectableCount controls how many options a user can pick.
curl -X POST "https://api.yourdomain.com/workspaces/be11c51b-1a5a-4bc9-9cb6-7ee7040ec4d8/proxy/api/sessions/my-session/messages/poll" \
-H "Authorization: Bearer wsk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"to": "923001234567",
"name": "Question?",
"options": ["Yes", "No", "Maybe"],
"selectableCount": 1
}'await fetch(
`https://api.yourdomain.com/workspaces/${workspaceId}/proxy/api/sessions/${sessionId}/messages/poll`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer wsk_live_xxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
to: '923001234567',
name: 'Question?',
options: ['Yes', 'No', 'Maybe'],
selectableCount: 1, // 1 = single choice; increase for multi-select
}),
}
);11. Buttons
Up to 3 reply buttons displayed beneath a message body. Button id max 20 chars, text max 20 chars.
curl -X POST "https://api.yourdomain.com/workspaces/be11c51b-1a5a-4bc9-9cb6-7ee7040ec4d8/proxy/api/sessions/my-session/messages/buttons" \
-H "Authorization: Bearer wsk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"to": "923001234567",
"text": "Choose:",
"footer": "WaSphere",
"buttons": [
{ "id": "btn1", "text": "Option A" },
{ "id": "btn2", "text": "Option B" }
]
}'await fetch(
`https://api.yourdomain.com/workspaces/${workspaceId}/proxy/api/sessions/${sessionId}/messages/buttons`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer wsk_live_xxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
to: '923001234567',
text: 'Choose:',
footer: 'WaSphere',
buttons: [
{ id: 'btn1', text: 'Option A' },
{ id: 'btn2', text: 'Option B' },
],
}),
}
);Button replies arrive as message.received webhooks with the clicked button's id in the payload.
12. List Message
Interactive list with sections and selectable rows. Requires a top-level title field. The recipient taps buttonText to open the full list.
curl -X POST "https://api.yourdomain.com/workspaces/be11c51b-1a5a-4bc9-9cb6-7ee7040ec4d8/proxy/api/sessions/my-session/messages/list" \
-H "Authorization: Bearer wsk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"to": "923001234567",
"title": "Choose your plan",
"text": "Select below:",
"buttonText": "View Options",
"sections": [
{
"title": "Plans",
"rows": [
{ "id": "free", "title": "Free", "description": "Basic" },
{ "id": "pro", "title": "Pro", "description": "Full access" }
]
}
]
}'await fetch(
`https://api.yourdomain.com/workspaces/${workspaceId}/proxy/api/sessions/${sessionId}/messages/list`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer wsk_live_xxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
to: '923001234567',
title: 'Choose your plan',
text: 'Select below:',
buttonText: 'View Options',
sections: [
{
title: 'Plans',
rows: [
{ id: 'free', title: 'Free', description: 'Basic' },
{ id: 'pro', title: 'Pro', description: 'Full access' },
],
},
],
}),
}
);When a recipient selects a row, you receive a message.received webhook with the selected row's id.
13. View Once
Send an image that disappears after the recipient views it once. Accepts the same url and optional caption fields as image.
curl -X POST "https://api.yourdomain.com/workspaces/be11c51b-1a5a-4bc9-9cb6-7ee7040ec4d8/proxy/api/sessions/my-session/messages/view-once" \
-H "Authorization: Bearer wsk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"to": "923001234567",
"url": "https://yourcdn.com/images/otp-screenshot.jpg",
"caption": "Your verification image"
}'await fetch(
`https://api.yourdomain.com/workspaces/${workspaceId}/proxy/api/sessions/${sessionId}/messages/view-once`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer wsk_live_xxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
to: '923001234567',
url: 'https://yourcdn.com/images/otp-screenshot.jpg',
caption: 'Your verification image',
}),
}
);14. Reaction
Add an emoji reaction to an existing message. Supply the messageId returned by a previous send. Send an empty string for emoji to remove a reaction.
curl -X POST "https://api.yourdomain.com/workspaces/be11c51b-1a5a-4bc9-9cb6-7ee7040ec4d8/proxy/api/sessions/my-session/messages/reaction" \
-H "Authorization: Bearer wsk_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"to": "923001234567",
"messageId": "3EB0C767D097B7C7A5C1",
"emoji": "👍"
}'await fetch(
`https://api.yourdomain.com/workspaces/${workspaceId}/proxy/api/sessions/${sessionId}/messages/reaction`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer wsk_live_xxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
to: '923001234567',
messageId: '3EB0C767D097B7C7A5C1',
emoji: '👍', // empty string "" removes the reaction
}),
}
);Rate Limits
The API enforces a global limit of 60 requests per minute per API key. When exceeded, the server responds with:
HTTP 429 Too Many Requests
Retry-After: 14
Use the Retry-After header value (seconds) to back off before retrying.
Sending via n8n
Use the HTTP Request node to send messages from an n8n workflow.
Node configuration:
| Field | Value |
|---|---|
| Method | POST |
| URL | https://api.yourdomain.com/workspaces/YOUR_WORKSPACE_ID/proxy/api/sessions/YOUR_SESSION_ID/messages/text |
| Authentication | None (add header manually) |
| Body Content Type | JSON |
Headers — add via "Header Parameters":
| Name | Value |
|---|---|
Authorization | Bearer wsk_live_xxxxx |
Content-Type | application/json |
Body (JSON):
{
"to": "{{ $json.phoneNumber }}",
"text": "Hello {{ $json.firstName }}, your order is ready!"
}
Reading the response — the node output will contain messageId and status:
// In a subsequent Code node:
const messageId = $input.first().json.messageId;
const status = $input.first().json.status;
Store your API key as an n8n credential (use "Header Auth" type) so it is never visible in plaintext in node configuration. Reference it in the Authorization header as Bearer {{ $credentials.wapiKey }}.
Error Responses
| Status | Description |
|---|---|
400 | Invalid request body — check field names and phone number format |
401 | Missing or invalid Authorization header |
404 | Session not found — check workspaceId and sessionId in the URL |
429 | Rate limit exceeded — respect the Retry-After header |
500 | Session is not connected — reconnect the session from the dashboard |