TwaBot REST API v1
Send WhatsApp messages, templates, media, and OTPs from your CRM or any application.
Growth plan: All endpoints available. Daily limit: 150 messages/day (admin adjustable).
Pro plan: All endpoints available. Daily limit: 5,000 messages/day (admin adjustable).
send-text, send-media, quick-send?message=) are only delivered by Meta if the recipient has messaged your business in the last 24 hours. Outside this window our API returns 400 with error_code: WINDOW_CLOSED so you know to use a pre-approved template instead. Pre-approved templates (send-template, quick-send?template=) work at any time.
delivery_status: "queued" — Meta acceptance is not the same as actual delivery. Poll GET /message-status/{message_id} for the real delivery state (sent → delivered → read / failed).
The TwaBot REST API lets you integrate WhatsApp messaging directly into your CRM, website, or backend system. You can send template messages, text replies, media, OTPs, fetch approved templates, and check delivery status — all programmatically.
Download Postman Collection Import this JSON in Postman → just change theapi_key variable to your key
Authentication
All API requests require an API key passed via the X-API-Key header. You can generate API keys from your TwaBot dashboard under API & Integrations.
curl -X GET https://twabot.com/api/v1/health \ -H "X-API-Key: twb_your_api_key_here"
Generating an API Key
1. Log in to your TwaBot dashboard
2. Go to API & Integrations in the sidebar
3. Click "Generate API Key"
4. Give it a name (e.g., "My CRM")
5. Select permissions (or choose "All Permissions")
6. Copy the key immediately — it won't be shown again
Base URL
All API endpoints use the following base URL:
https://twabot.com/api/v1
Rate Limits
Three levels of rate limiting are applied:
| Scope | Limit | Window |
|---|---|---|
| Global (all API keys combined) | 120 requests | 1 minute |
| Per API key (configurable) | 60 requests (default) | 1 minute |
| Daily limit per API key | 150 (Growth) / 5,000 (Pro) | Per day (resets midnight) |
When rate limited, the API returns a 429 status code with:
{
"error": "Rate limit exceeded. Please slow down.",
"retry_after_seconds": 60
}
Daily limit exceeded response:
{
"success": false,
"error": "Daily send limit reached (150/day on Growth plan). Resets at midnight. Upgrade to Pro for higher limits.",
"daily_limit": 150,
"daily_used": 150,
"plan": "Growth",
"retry_after": "midnight"
}
Per-minute and daily limits can be configured per API key by your admin. Contact support to request a custom limit.
Error Handling
All errors follow a consistent format:
{
"success": false,
"error": "Human-readable error description."
}
For successful responses:
{
"success": true,
"message_id": "wamid.xxxx",
...
}
Endpoint Access by Plan
| Endpoint | Growth Plan | Pro Plan | 24h window required? |
|---|---|---|---|
GET /health | ✅ | ✅ | — |
GET /templates | ✅ | ✅ | — |
GET /quick-send?template= | ✅ (150/day) | ✅ (5,000/day) | No (works anytime) |
GET /quick-send?message= | ✅ (150/day) | ✅ (5,000/day) | Yes |
POST /send-template | ✅ (150/day) | ✅ (5,000/day) | No (works anytime) |
POST /send-text | ✅ (150/day) | ✅ (5,000/day) | Yes |
POST /send-media | ✅ (150/day) | ✅ (5,000/day) | Yes |
POST /send-otp | ✅ (150/day) | ✅ (5,000/day) | No (uses template) |
GET /message-status | ✅ | ✅ | — |
All send endpoints are available on both plans; the difference is the daily send cap and the 24-hour window rule for free-form messages. Starter plan users get a 403 on all send endpoints.
What the 24-hour window means
WhatsApp Business Platform only allows free-form text/media messages to be delivered within 24 hours of the recipient's last message to your business. For re-engagement, shipment notifications, OTPs and similar outbound use cases, use a Meta-approved template — templates work at any time.
When the window is closed, our API refuses the send instead of returning a fake success:
{
"success": false,
"error": "Recipient is outside the 24-hour customer service window. Free-form text messages can only be delivered within 24 hours of the contact messaging your business. Use a pre-approved template for re-engagement.",
"error_code": "WINDOW_CLOSED",
"session_window": { "open": false, "last_inbound_at": null },
"hint": "Call POST /api/v1/send-template or GET /api/v1/quick-send?phone=...&template=... instead."
}
GET /health
Validate your API key and check if WhatsApp is connected. Use this to test your integration before going live.
curl -X GET https://twabot.com/api/v1/health \ -H "X-API-Key: twb_your_api_key_here"
{
"success": true,
"business": "Your Business Name",
"whatsapp_connected": true,
"api_key_name": "My CRM",
"timestamp": "2026-04-02T10:30:00.000Z"
}
GET /quick-send
A simple GET-based endpoint designed for CRMs, form builders, and automation tools that cannot send POST requests with JSON bodies. Works exactly like BhashSMS / MSG91 style URLs — just pass everything as query parameters.
Permission required:
send_template or *24-hour window: Required only when using
message= (free-form text). Not required for template=.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
apikey | string | Required | Your API key (twb_xxx...). Alternative to the X-API-Key header. |
phone | string | Required | Customer's phone number with country code (e.g., 919876543210) |
template | string | Conditional | Meta-approved template name. Required if message is not provided. |
lang | string | Optional | Template language code. Default: en |
params | string | Optional | Comma-separated template variables. E.g., Rahul,ORD-123,₹1499 |
message | string | Conditional | Plain text message (only works within the 24-hour conversation window). Required if template is not provided. |
Example — Template Send (BhashSMS style)
GET https://twabot.com/api/v1/quick-send?apikey=twb_your_api_key_here&phone=919876543210&template=order_confirmation&lang=en¶ms=Rahul,ORD-12345,₹1499
Example — Text Message (24h window)
GET https://twabot.com/api/v1/quick-send?apikey=twb_your_api_key_here&phone=919876543210&message=Your+order+has+been+shipped
{
"success": true,
"message_id": "wamid.HBgMOTE5ODc2NTQzMjEw...",
"phone": "919876543210",
"template": "order_confirmation",
"delivery_status": "queued",
"note": "Message accepted by Meta. Actual delivery is asynchronous — poll GET /api/v1/message-status/{message_id} to track delivered/read/failed."
}
{
"success": true,
"message_id": "wamid.HBgMOTE5ODc2NTQzMjEw...",
"phone": "919876543210",
"delivery_status": "queued",
"note": "Message accepted by Meta. Actual delivery is asynchronous — poll GET /api/v1/message-status/{message_id} to track delivered/read/failed."
}
{
"success": false,
"error": "Recipient is outside the 24-hour customer service window. Free-form text messages can only be delivered within 24 hours of the contact messaging your business. Use a pre-approved template for re-engagement.",
"error_code": "WINDOW_CLOSED",
"session_window": { "open": false, "last_inbound_at": null },
"hint": "Call GET /api/v1/quick-send?phone=...&template=<approved_template_name>¶ms=... instead."
}
{
"success": false,
"error": "Phone 919876543210 is suppressed (opted_out). Message not sent to protect your quality rating.",
"suppression_state": "opted_out"
}
{
"success": false,
"error": "Daily send limit reached (150/day on Growth plan). Resets at midnight. Upgrade to Pro for higher limits.",
"daily_limit": 150,
"daily_used": 150,
"plan": "Growth",
"retry_after": "midnight"
}
phone and params parameters.
No coding required.
POST /send-template
Send a Meta-approved WhatsApp template message. This is the primary endpoint for sending messages to customers who haven't messaged you in the last 24 hours.
send_template or *
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
phone | string | Required | Customer's phone number (see Phone Format) |
template_name | string | Required | Meta template name (e.g., order_confirmation) |
language | string | Optional | Template language code. Default: en |
body_params | array | Optional | Values for body variables {{1}}, {{2}}, etc. |
header_params | array | Optional | Values for header variables |
Example Request
curl -X POST https://twabot.com/api/v1/send-template \
-H "X-API-Key: twb_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"phone": "919876543210",
"template_name": "order_confirmation",
"language": "en",
"body_params": ["Rahul", "ORD-12345", "₹1,499"]
}'
{
"success": true,
"message_id": "wamid.HBgMOTE5ODc2NTQzMjEw...",
"phone": "919876543210",
"template": "order_confirmation",
"delivery_status": "queued",
"note": "Message accepted by Meta. Actual delivery is asynchronous — poll GET /api/v1/message-status/{message_id} to track delivered/read/failed."
}
{
"success": false,
"error": "Template 'wrong_name' not found or not approved."
}
POST /send-text
Send a plain text message. This only works within the 24-hour conversation window — the recipient must have messaged your business within the last 24 hours.
400 with error_code: WINDOW_CLOSED. Use Send Template for re-engagement.
Permission required:
send_text or *
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
phone | string | Required | Customer's phone number |
message | string | Required | Message text (max 4096 characters) |
Example Request
curl -X POST https://twabot.com/api/v1/send-text \
-H "X-API-Key: twb_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"phone": "9876543210",
"message": "Hi Rahul! Your appointment is confirmed for tomorrow at 2 PM."
}'
{
"success": true,
"message_id": "wamid.HBgMOTE5ODc2NTQzMjEw...",
"phone": "919876543210",
"delivery_status": "queued",
"note": "Message accepted by Meta. Actual delivery is asynchronous — poll GET /api/v1/message-status/{message_id} to track delivered/read/failed."
}
{
"success": false,
"error": "Recipient is outside the 24-hour customer service window. Free-form text messages can only be delivered within 24 hours of the contact messaging your business. Use a pre-approved template (send-template or quick-send with template=) for re-engagement.",
"error_code": "WINDOW_CLOSED",
"session_window": { "open": false, "last_inbound_at": null },
"hint": "Call POST /api/v1/send-template or GET /api/v1/quick-send?phone=...&template=... instead."
}
POST /send-media
Send a media message (image, document, video, or audio). Requires the 24-hour conversation window.
400 with error_code: WINDOW_CLOSED. Use a media-header template via Send Template for re-engagement.
Permission required:
send_media or *
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
phone | string | Required | Customer's phone number |
media_type | string | Required | One of: image, document, video, audio |
media_url | string | Required | Publicly accessible URL of the media file |
caption | string | Optional | Caption for image/video/document |
filename | string | Optional | Filename for documents (e.g., invoice.pdf) |
Example Request
curl -X POST https://twabot.com/api/v1/send-media \
-H "X-API-Key: twb_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"phone": "919876543210",
"media_type": "document",
"media_url": "https://example.com/invoice-123.pdf",
"caption": "Here is your invoice",
"filename": "Invoice-123.pdf"
}'
{
"success": true,
"message_id": "wamid.HBgMOTE5ODc2NTQzMjEw...",
"phone": "919876543210",
"media_type": "document",
"delivery_status": "queued",
"note": "Message accepted by Meta. Actual delivery is asynchronous — poll GET /api/v1/message-status/{message_id} to track delivered/read/failed."
}
POST /send-otp
Generate and send a one-time password (OTP) via an approved OTP template. If you don't provide an OTP, TwaBot auto-generates a 6-digit code. Since OTP uses a template, it works outside the 24-hour window.
Permission required:
send_otp or *
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
phone | string | Required | Customer's phone number |
template_name | string | Required | Your approved OTP template name |
otp | string | Optional | Custom OTP code. Auto-generated if not provided. |
language | string | Optional | Template language code. Default: en |
Example Request
curl -X POST https://twabot.com/api/v1/send-otp \
-H "X-API-Key: twb_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"phone": "919876543210",
"template_name": "otp_verification"
}'
{
"success": true,
"message_id": "wamid.HBgMOTE5ODc2NTQzMjEw...",
"phone": "919876543210",
"otp": "482913",
"delivery_status": "queued",
"note": "Message accepted by Meta. Actual delivery is asynchronous — poll GET /api/v1/message-status/{message_id} to track delivered/read/failed."
}
GET /templates
Get all Meta-approved WhatsApp templates for your business. Use this to see available template names and their variables before calling send-template.
Example Request
curl -X GET https://twabot.com/api/v1/templates \ -H "X-API-Key: twb_your_api_key_here"
{
"success": true,
"templates": [
{
"id": 12,
"name": "order_confirmation",
"display_name": "Order Confirmation",
"content": "Hi {{1}}, your order {{2}} of {{3}} has been confirmed!",
"category": "UTILITY",
"language": "en",
"header_type": null,
"header_text": null,
"footer_text": "TwaBot - WhatsApp Business",
"variables": ["customer_name", "order_id", "amount"]
}
]
}
GET /message-status/:messageId
Check the delivery status of a sent message using its message_id (returned from any send endpoint).
Example Request
curl -X GET https://twabot.com/api/v1/message-status/wamid.HBgMOTE5ODc2NTQzMjEw... \ -H "X-API-Key: twb_your_api_key_here"
{
"success": true,
"message_id": "wamid.HBgMOTE5ODc2NTQzMjEw...",
"api_status": "success",
"delivery_status": "delivered",
"sent_at": "2026-04-14T10:30:45.000Z",
"to": "919876543210"
}
{
"success": true,
"message_id": "wamid.HBgMOTE5ODc2NTQzMjEw...",
"api_status": "success",
"delivery_status": "failed",
"sent_at": "2026-04-14T10:30:45.000Z",
"to": "919876543210",
"failure_reason": "Message undeliverable — recipient's phone is not on WhatsApp."
}
{
"success": false,
"error": "Message not found.",
"hint": "Status is available within a few seconds of sending. If message_id is correct, retry shortly."
}
The response returns two status fields:
| Field | Values | Meaning |
|---|---|---|
api_status | success / failed | Our side — whether we accepted the send and forwarded it to Meta. |
delivery_status | sent / delivered / read / failed | Meta side — updated from Meta's delivery webhooks. |
Possible delivery_status values:
| Status | Meaning |
|---|---|
sent | Message handed to Meta, awaiting delivery. |
delivered | Message delivered to the recipient's phone (one grey tick → two grey ticks). |
read | Recipient has opened the message (blue ticks). |
failed | Delivery failed. failure_reason explains why. |
success: true in the send response only means Meta accepted the API call. For the real delivery state, query /message-status/{message_id}.
Phone Number Format
TwaBot automatically normalizes phone numbers. You can send in any of these formats:
| Input | Normalized To |
|---|---|
9876543210 | 919876543210 (auto-adds 91 for India) |
+91-9876543210 | 919876543210 |
+91 98765 43210 | 919876543210 |
919876543210 | 919876543210 (no change) |
+14155551234 | 14155551234 (international) |
Rules: Spaces, hyphens, parentheses, and + are stripped. A 10-digit number starting with 6-9 is assumed to be Indian and gets the 91 prefix.
API Key Permissions
Each API key can have granular permissions controlling which endpoints it can access:
| Permission | Endpoints Allowed |
|---|---|
send_template | POST /send-template |
send_text | POST /send-text |
send_media | POST /send-media |
send_otp | POST /send-otp |
* | All endpoints (full access) |
GET /health, GET /templates, and GET /message-status do not require specific permissions — any valid API key can access them.
HTTP Status Codes
| Code | Meaning |
|---|---|
| 200 | Success |
| 400 | Bad request — invalid input, missing required fields, or WhatsApp not connected |
| 401 | Unauthorized — missing, invalid, or expired API key |
| 403 | Forbidden — API key lacks required permission |
| 404 | Not found — message or resource does not exist |
| 429 | Rate limit exceeded |
| 500 | Internal server error |
Need help? Contact us at support@twabot.com
Also see: Webhooks Documentation →