Inbound Messages Webhook
When someone messages your linked WhatsApp number, Rapiwa POSTs the message to the Webhook URL you set on the device (in the dashboard). This is how you build bots, auto-replies, and inbox integrations.
- Direction: Rapiwa → your server
- Method:
POSTwith a JSON body - Configure: set the device's Webhook URL in the dashboard
- Reliability: deliveries retry with backoff on failure
Payload — receive-message
{
"session_id": "1024",
"from": "8801234567890@s.whatsapp.net",
"contact_id": "8801234567890",
"name": "Sender Name",
"message_id": "3EB0A1B2C3…",
"message_type": "text",
"message": "Hi, is this available?",
"timestamp": "1755923552",
"media_info": []
}
| Field | Description |
|---|---|
session_id | The device that received the message |
from | Sender JID |
contact_id | Sender's phone number (digits only) |
name | Sender's WhatsApp display name |
message_id | Message id — pass to delete / quote when you reply |
message_type | text · image · video · audio · document · location · interactive · sticker |
message | Body / caption. For location, a https://www.google.com/maps?q=lat,lng URL |
timestamp | Unix seconds (string) |
media_info | See below |
media_info
| Message type | media_info shape |
|---|---|
| Text | [] (empty) |
| Media (image/video/audio/document) | { "mimetype": "...", "fileName": "...", "file_url": "https://…" } |
| Location | { "latitude": 37.77, "longitude": -122.41 } |
For media, Rapiwa downloads the file, stores it, and gives you a public file_url — you don't need to fetch it from WhatsApp yourself.
Responding
Reply by calling POST /api/send-message with the sender's number. To thread the reply, pass the inbound message_id as quoted_message_id.
// Express example
app.post('/rapiwa/webhook', async (req, res) => {
const { contact_id, message, message_id } = req.body
res.sendStatus(200) // ack fast
if (/price/i.test(message)) {
await fetch('https://app.rapiwa.com/api/send-message', {
method: 'POST',
headers: { Authorization: `Bearer ${DEVICE_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
number: contact_id,
message_type: 'text',
message: 'Our price list: https://example.com/pricing',
quoted_message_id: message_id,
}),
})
}
})
Verifying deliveries (recommended)
Legacy webhooks are unsigned. Rapiwa can additionally send an X-Rapiwa-Signature header — an HMAC of the raw request body — so you can confirm a delivery really came from Rapiwa. Compute the same HMAC with your webhook secret and compare. Keep accepting unsigned bodies only if you must support older setups.
Ack quickly
Return 200 as soon as you've received the payload, then do slow work (DB writes, replies) asynchronously. Non-200 responses cause Rapiwa to retry.
Delivery gating
Inbound delivery requires your tenant wallet balance to be above the minimum. If the balance is too low, delivery is paused until you top up — see Credits.