Retries, built in.
Up to 8 attempts for eligible failures. Manual redelivery when you need another go.
WhatsApp gives you long polling. We give you signed webhooks.
Connect your agent, add your endpoint, and get back to building.
Illustration: a customer sends your WhatsApp agent a message. Unpoll long-polls the WhatsApp Agent API, stores the message and delivers it to your app as a signed webhook. Your app replies with one API call to Unpoll, which sends the text back, and the customer sees the reply in WhatsApp.
{
"type": "message.received",
"data": {
"from": "user:50972923564215",
"text": { "body": "Hello agent" }
}
}
{
"to": "user:50972923564215",
"text": "Hi! I’m on it. What do you need?"
}
Illustrative flow · Webhook delivery runs independently of polling, and replies go out through one API call with a per-agent key.
No always-on worker to babysit. No cursor to recover.
Just an HTTPS endpoint and the code you actually want to write.
Add your WhatsApp Agent API token. Unpoll handles continuous polling and cursor tracking.
Your token stays encrypted.Set your HTTPS webhook URL. Incoming messages arrive as signed events.
Verify. Deduplicate. Acknowledge.Run your logic, send a text reply, or mark a message read. We keep the events moving.
Follow the quickstartEvents are stored before the cursor moves forward. Delivery runs separately, with a clear record of what happened.
Understand the delivery guaranteesUp to 8 attempts for eligible failures. Manual redelivery when you need another go.
Inspect responses, timing, and failures in your dashboard, with 7-day event history.
HMAC-SHA256 signatures and stable event IDs. At-least-once delivery you can safely deduplicate.
Your language. Your stack. Your bot logic. Reply through one straightforward API, using a per-agent key.
Built for creator-only messaging on the WhatsApp Agent API. See the boundaries.
Rust
let sent = client
.post(format!("{base}/v1/agents/{agent_id}/messages"))
.bearer_auth(api_key)
.json(&json!({ "to": sender, "text": "Hello!" }))
.send().await?
.json::<Value>().await?;
Python
sent = requests.post(
f"{base}/v1/agents/{agent_id}/messages",
headers={"Authorization": f"Bearer {api_key}"},
json={"to": sender, "text": "Hello!"},
).json()
Go
payload, _ := json.Marshal(map[string]string{"to": sender, "text": "Hello!"})
req, _ := http.NewRequest("POST", url, bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
response, _ := client.Do(req)
Node.js
const response = await fetch(url, {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json" },
body: JSON.stringify({ to: sender, text: "Hello!" }),
});
const { message_id } = await response.json();
cURL
curl "$BASE_URL/v1/agents/$AGENT_ID/messages" \
-H "Authorization: Bearer $UNPOLL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"to":"user:50972923564215","text":"Hello!"}'
Every feature on every plan. Just choose your agent count.
Build your first integration.
Run a few bots and projects.
Manage more agents in one workspace.
Prices in USD, billed monthly. All plans include every feature.
The WhatsApp Agent API exposes updates through long polling. Unpoll runs that loop for you, stores new events, and sends signed webhooks to your app. You build the agent’s behavior without maintaining a polling worker.
No. The WhatsApp Agent API supports creator-only messaging: an agent can message only the person who created it. Unpoll supports text replies, read receipts, and downloads of received media. It does not send media or provide broadcast messaging.
Polling continues independently. Eligible failed deliveries retry up to 8 attempts; without Retry-After, persistent errors typically exhaust those attempts in roughly 1 to 3.5 minutes. For longer outages, manually redeliver retained failures from the dashboard. Read the retry policy.
Only updates timestamped after activation are eligible. Resuming a paused agent sets a new cutoff, so paused-period messages are skipped. Set up your webhook before messaging the agent: events received without an enabled endpoint are not backfilled. See the integration limits.