OpenWA API Reference for n8n Workflows
Every message type the OpenWA API supports, with exact n8n HTTP Request node configuration. All endpoints require the X-API-Key header (your OpenWA admin key). Base URL inside Docker: http://openwa-api:2785.
Authentication setup in n8n: HTTP Header Auth credential with name X-API-Key and value = your OpenWA API key. Reuse the same credential across all HTTP Request nodes.
Base URL Pattern
http://openwa-api:2785/api/sessions/{sessionId}/messages/...
In n8n's HTTP Request node, use the dynamic expression to pull sessionId from the webhook payload:
http://openwa-api:2785/api/sessions/{{ $('WA Webhook').item.json.body.sessionId }}/messages/send-text
The sessionId comes from the webhook payload at body.sessionId (not from body.data.sessionId — that path is for the session name).
Quick Reference: chatId Formats
| Type | Format | Example |
|---|---|---|
| Individual | phone@c.us |
628123456789@c.us |
| Group | groupId@g.us |
123456789-987654@g.us |
The webhook payload at body.data.chatId already uses the correct format — pass it directly from the incoming data.
Mapping Webhook Payload to API Calls
The incoming webhook payload structure (at body.data.*):
body
├── sessionId: "default"
└── data
├── id: "true_628123456789@c.us_3EB0ABC123" ← messageId
├── from: "628123456789@c.us" ← sender chatId
├── fromMe: false
├── chatId: "628123456789@c.us" ← use this as recipient
├── body: "the message text"
├── pushName: "John Doe"
├── isGroup: false
└── timestamp: 1706860800
For replies, use body.data.id as quotedMessageId. For the recipient, always use body.data.chatId (not body.data.from — they differ in group contexts).
Send Text Message
Endpoint: POST /api/sessions/:sessionId/messages/send-text
HTTP Request node:
=http://openwa-api:2785/api/sessions/{{ $('WA Webhook').item.json.body.sessionId }}/messages/send-textBody:
{
"chatId": "{{ $json.body.data.chatId }}",
"text": "Hello from your n8n workflow!"
}
The webhook delivers body.data.chatId (e.g. 628123456789@c.us). The sessionId in the URL is body.sessionId (e.g. default).
Options (optional):
{
"chatId": "628123456789@c.us",
"text": "Reply text",
"options": {
"quotedMessageId": "true_628123456789@c.us_3EB0ABC123",
"mentionedIds": ["628111111111@c.us"]
}
}
quotedMessageId — reply to a specific message (pass the messageId from the incoming webhook payload)mentionedIds — array of contact IDs to @mention in the messageResponse:
{
"messageId": "true_628123456789@c.us_3EB0ABC123",
"status": "sent",
"timestamp": "2025-02-02T10:00:00.000Z"
}
Send Image
Endpoint: POST /api/sessions/:sessionId/messages/send-image
HTTP Request node:
=http://openwa-api:2785/api/sessions/{{ $('WA Webhook').item.json.body.sessionId }}/messages/send-imageFrom URL:
{
"chatId": "{{ $json.body.data.chatId }}",
"url": "https://example.com/image.jpg",
"caption": "Check this out!"
}
From Base64 (e.g. AI-generated image):
{
"chatId": "{{ $json.body.data.chatId }}",
"base64": "data:image/jpeg;base64,/9j/4AAQ...",
"mimetype": "image/jpeg",
"caption": "AI generated image"
}
| Field | Required | Description |
|---|---|---|
chatId |
Yes | Recipient WhatsApp ID |
url |
Yes (unless base64) | Public URL to the image |
base64 |
Yes (unless url) | Base64 string with mime prefix |
mimetype |
Yes (if base64) | e.g. image/jpeg, image/png, image/webp |
caption |
No | Text caption under the image (max 1024 chars) |
Send Video
Endpoint: POST /api/sessions/:sessionId/messages/send-video
HTTP Request node:
=http://openwa-api:2785/api/sessions/{{ $('WA Webhook').item.json.body.sessionId }}/messages/send-videoFrom URL:
{
"chatId": "{{ $json.body.data.chatId }}",
"url": "https://example.com/video.mp4",
"caption": "Watch this video!"
}
From Base64:
{
"chatId": "{{ $json.body.data.chatId }}",
"base64": "data:video/mp4;base64,...",
"mimetype": "video/mp4",
"caption": "Video caption"
}
| Field | Required | Description |
|---|---|---|
chatId |
Yes | Recipient WhatsApp ID |
url |
Yes (unless base64) | Public URL to the video |
base64 |
Yes (unless url) | Base64 string with mime prefix |
mimetype |
Yes (if base64) | e.g. video/mp4, video/3gpp |
caption |
No | Text caption (max 1024 chars) |
Send Audio / Voice Note
Endpoint: POST /api/sessions/:sessionId/messages/send-audio
HTTP Request node:
=http://openwa-api:2785/api/sessions/{{ $('WA Webhook').item.json.body.sessionId }}/messages/send-audioFrom URL:
{
"chatId": "{{ $json.body.data.chatId }}",
"url": "https://example.com/audio.mp3"
}
Voice note (push-to-talk):
{
"chatId": "{{ $json.body.data.chatId }}",
"url": "https://example.com/voice-note.mp3",
"ptt": true
}
| Field | Required | Description |
|---|---|---|
chatId |
Yes | Recipient WhatsApp ID |
url |
Yes (unless base64) | Public URL to the audio |
base64 |
Yes (unless url) | Base64 audio data |
mimetype |
Yes (if base64) | e.g. audio/mp4, audio/ogg, audio/mpeg |
ptt |
No | true = send as voice note (default: false) |
Send Document / File
Endpoint: POST /api/sessions/:sessionId/messages/send-document
HTTP Request node:
=http://openwa-api:2785/api/sessions/{{ $('WA Webhook').item.json.body.sessionId }}/messages/send-documentFrom URL:
{
"chatId": "{{ $json.body.data.chatId }}",
"url": "https://example.com/report.pdf",
"filename": "monthly-report.pdf",
"caption": "Your monthly report is attached"
}
| Field | Required | Description |
|---|---|---|
chatId |
Yes | Recipient WhatsApp ID |
url |
Yes (unless base64) | Public URL to the file |
base64 |
Yes (unless url) | Base64 file data |
mimetype |
Yes (if base64) | e.g. application/pdf, application/vnd.ms-excel |
filename |
No | Display name of the file (defaults to URL filename) |
caption |
No | Text caption above the file |
Reply to a Message
Endpoint: POST /api/sessions/:sessionId/messages/reply
Use this instead of send-text with options.quotedMessageId — it is purpose-built for replies and easier to configure in n8n.
HTTP Request node:
=http://openwa-api:2785/api/sessions/{{ $('WA Webhook').item.json.body.sessionId }}/messages/replyBody:
{
"chatId": "{{ $json.body.data.chatId }}",
"quotedMessageId": "{{ $json.body.data.id }}",
"text": "This is a reply to your message"
}
| Field | Required | Description |
|---|---|---|
chatId |
Yes | Recipient WhatsApp ID |
quotedMessageId |
Yes | The id field from the incoming webhook message you are replying to |
text |
Yes | Reply text content |
React to a Message (Emoji)
Endpoint: POST /api/sessions/:sessionId/messages/react
HTTP Request node:
=http://openwa-api:2785/api/sessions/{{ $('WA Webhook').item.json.body.sessionId }}/messages/reactBody:
{
"chatId": "{{ $json.body.data.chatId }}",
"messageId": "{{ $json.body.data.id }}",
"emoji": "👍"
}
| Field | Required | Description |
|---|---|---|
chatId |
Yes | The chat this message belongs to |
messageId |
Yes | The id of the message to react to |
emoji |
Yes | Any emoji string. Send empty string `` to remove the reaction |
Send Location
Endpoint: POST /api/sessions/:sessionId/messages/send-location
HTTP Request node:
=http://openwa-api:2785/api/sessions/{{ $('WA Webhook').item.json.body.sessionId }}/messages/send-locationBody:
{
"chatId": "{{ $json.body.data.chatId }}",
"latitude": -6.2088,
"longitude": 106.8456,
"description": "Jakarta, Indonesia"
}
| Field | Required | Description |
|---|---|---|
chatId |
Yes | Recipient WhatsApp ID |
latitude |
Yes | Latitude coordinate |
longitude |
Yes | Longitude coordinate |
description |
No | Location name / address string |
Send Contact Card
Endpoint: POST /api/sessions/:sessionId/messages/send-contact
HTTP Request node:
=http://openwa-api:2785/api/sessions/{{ $('WA Webhook').item.json.body.sessionId }}/messages/send-contactBody:
{
"chatId": "{{ $json.body.data.chatId }}",
"contactName": "John Doe",
"contactNumber": "628987654321"
}
| Field | Required | Description |
|---|---|---|
chatId |
Yes | Recipient WhatsApp ID |
contactName |
Yes | Display name of the contact card |
contactNumber |
Yes | Phone number (without + prefix, just digits) |
Send Sticker
Endpoint: POST /api/sessions/:sessionId/messages/send-sticker
Same body structure as Send Image — WhatsApp accepts sticker packs as media. Useful for branded acknowledgements, order milestones, or reaction-style automations.
HTTP Request node:
=http://openwa-api:2785/api/sessions/{{ $('WA Webhook').item.json.body.sessionId }}/messages/send-stickerBody (from URL):
{
"chatId": "{{ $json.body.data.chatId }}",
"url": "https://example.com/stickers/thanks.webp",
"mimetype": "image/webp"
}
Body (from base64 — e.g. AI-generated sticker):
{
"chatId": "{{ $json.body.data.chatId }}",
"base64": "data:image/webp;base64,...",
"mimetype": "image/webp"
}
| Field | Required | Description |
|---|---|---|
chatId |
Yes | Recipient WhatsApp ID |
url |
Yes (unless base64) | Public URL to the sticker (.webp, .png, .gif) |
base64 |
Yes (unless url) | Base64 sticker data |
mimetype |
Yes (if base64) | image/webp, image/png, image/gif |
Send Template Messages
Endpoint: POST /api/sessions/:sessionId/messages/send-template
Send templated messages with variable substitution. Useful for order confirmations, appointment reminders, shipping updates, or any notification that needs a consistent format. Templates are defined in the WhatsApp Business Manager and referenced by name or ID.
HTTP Request node:
=http://openwa-api:2785/api/sessions/{{ $('WA Webhook').item.json.body.sessionId }}/messages/send-templateBy template name:
{
"chatId": "{{ $json.body.data.chatId }}",
"templateName": "order-confirmation",
"vars": {
"customer": "{{ $json.body.data.pushName }}",
"orderId": "ORD-12345",
"amount": "₹1,299"
}
}
By template ID:
{
"chatId": "{{ $json.body.data.chatId }}",
"templateId": "b1c2d3e4-f5a6-7890-bcde-f01234567890",
"vars": {
"otp": "4829"
}
}
| Field | Required | Description |
|---|---|---|
chatId |
Yes | Recipient WhatsApp ID |
templateName |
Yes (or templateId) | Template name as registered in WhatsApp Business Manager |
templateId |
Yes (or templateName) | UUID of the template |
vars |
No | Key-value pairs matching {{placeholder}} tokens in your template |
Bulk Broadcast
Endpoint: POST /api/sessions/:sessionId/messages/send-bulk
Send personalised messages to up to 100 recipients in one API call. Each recipient gets different content — variables are substituted per message. Messages are processed asynchronously with configurable delay to avoid rate limiting.
HTTP Request node:
=http://openwa-api:2785/api/sessions/{{ $('WA Webhook').item.json.body.sessionId }}/messages/send-bulkBody:
{
"messages": [
{
"chatId": "628123456789@c.us",
"type": "text",
"content": {
"text": "Hi {{customer}}, your order #{{orderId}} has shipped!"
},
"variables": {
"customer": "Alice",
"orderId": "ORD-001"
}
},
{
"chatId": "628198765432@c.us",
"type": "text",
"content": {
"text": "Hi {{customer}}, your order #{{orderId}} has shipped!"
},
"variables": {
"customer": "Bob",
"orderId": "ORD-002"
}
},
{
"chatId": "628177777777@c.us",
"type": "image",
"content": {
"image": { "url": "https://example.com/promo.jpg" },
"caption": "{{customer}}, check out today's deal!"
},
"variables": {
"customer": "Carol"
}
}
],
"options": {
"delayBetweenMessages": 5000,
"randomizeDelay": true,
"stopOnError": false
}
}
Track batch status — use a second HTTP Request node (GET) after submitting:
=http://openwa-api:2785/api/sessions/{{ $('WA Webhook').item.json.body.sessionId }}/messages/batch/{{ $json.batchId }}Response from send-bulk:
{
"batchId": "batch_abc123",
"status": "processing",
"totalMessages": 3,
"estimatedCompletionTime": "2025-02-02T10:05:00.000Z",
"statusUrl": "/api/sessions/default/messages/batch/batch_abc123"
}
| Field | Required | Description |
|---|---|---|
messages |
Yes | Array of up to 100 message objects |
messages[].chatId |
Yes | Recipient WhatsApp ID |
messages[].type |
Yes | text, image, video, audio, or document |
messages[].content.text |
Yes (for text) | Message body — supports {{variable}} substitution |
messages[].content.image |
Yes (for image) | { url } or { base64, mimetype } |
messages[].variables |
No | Per-recipient variable values |
options.delayBetweenMessages |
No | Milliseconds between each send (min: 1000, default: 3000) |
options.randomizeDelay |
No | Adds 0–2s random jitter (default: true) |
options.stopOnError |
No | Halt batch on first failure (default: false) |
Typing / Recording Indicator
Endpoint: POST /api/sessions/:sessionId/chats/typing
Show a "typing..." or "recording..." indicator in the chat before your response arrives. Pair with an n8n Wait / Delay node (2–4 seconds) between this call and the actual reply to simulate a natural conversation flow.
HTTP Request node:
=http://openwa-api:2785/api/sessions/{{ $('WA Webhook').item.json.body.sessionId }}/chats/typingBody:
{
"chatId": "{{ $json.body.data.chatId }}",
"state": "typing"
}
Use state: "recording" for voice-note style bots. Use state: "paused" to clear the indicator (usually sent after your message arrives).
Delete / Recall a Message
Endpoint: POST /api/sessions/:sessionId/messages/delete
Delete a message you sent — for example an OTP sent by mistake, or a message containing sensitive information.
HTTP Request node:
=http://openwa-api:2785/api/sessions/{{ $('WA Webhook').item.json.body.sessionId }}/messages/deleteBody:
{
"chatId": "{{ $json.body.data.chatId }}",
"messageId": "{{ $json.body.data.id }}",
"forEveryone": true
}
| Field | Required | Description |
|---|---|---|
chatId |
Yes | Chat the message belongs to |
messageId |
Yes | The id of the message to delete |
forEveryone |
No | true = delete for all participants (default); false = delete only for yourself |
Forward a Message
Endpoint: POST /api/sessions/:sessionId/messages/forward
Forward an incoming message to another chat — useful for bot triage (forward to human agent), escalation workflows, or cross-group announcements.
HTTP Request node:
=http://openwa-api:2785/api/sessions/{{ $('WA Webhook').item.json.body.sessionId }}/messages/forwardBody:
{
"fromChatId": "{{ $json.body.data.chatId }}",
"toChatId": "628111111111@c.us",
"messageId": "{{ $json.body.data.id }}"
}
| Field | Required | Description |
|---|---|---|
fromChatId |
Yes | Source chat (where the message currently is) |
toChatId |
Yes | Destination chat (where to forward it) |
messageId |
Yes | The id of the message to forward |
Quick Reference: Session & Chat Management
| Endpoint | Method | Description |
|---|---|---|
/api/sessions/:sessionId/qr |
GET | Get current QR code (for re-authentication) |
/api/sessions/:sessionId/chats/read |
POST | Mark a chat as read |
/api/sessions/:sessionId/chats/:chatId/history |
GET | Fetch message history for a chat |
/api/sessions/:sessionId/chats/typing |
POST | Send typing/recording indicator |
Related Posts
Built something similar or want to talk through the architecture? Get in touch.