TwaBot REST API v1

Send WhatsApp messages, templates, media, and OTPs from your CRM or any application.

Plan Requirement: API access is available on Growth and Pro plans. Starter has no API access.
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).
⚠️ Meta 24-hour customer-service window: Free-form text and media messages (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: Every successful send returns 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 the api_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"
Keep your key secret. API keys are shown only once when created. Store them securely. Never expose them in frontend code or public repositories.

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:

ScopeLimitWindow
Global (all API keys combined)120 requests1 minute
Per API key (configurable)60 requests (default)1 minute
Daily limit per API key150 (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

EndpointGrowth PlanPro Plan24h 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 Response (200)
{
  "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.

Plans: Available on both Growth (150/day) and Pro (5,000/day) plans.
Permission required: send_template or *
24-hour window: Required only when using message= (free-form text). Not required for template=.

Query Parameters

ParameterTypeRequiredDescription
apikeystringRequiredYour API key (twb_xxx...). Alternative to the X-API-Key header.
phonestringRequiredCustomer's phone number with country code (e.g., 919876543210)
templatestringConditionalMeta-approved template name. Required if message is not provided.
langstringOptionalTemplate language code. Default: en
paramsstringOptionalComma-separated template variables. E.g., Rahul,ORD-123,₹1499
messagestringConditionalPlain 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&params=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 Response — Template (200)
{
  "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 Response — Text / inside 24h window (200)
{
  "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."
}
Window Closed — Text outside 24h (400)
{
  "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>&params=... instead."
}
Suppression Error (403)
{
  "success": false,
  "error": "Phone 919876543210 is suppressed (opted_out). Message not sent to protect your quality rating.",
  "suppression_state": "opted_out"
}
Daily Limit Exceeded (429)
{
  "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"
}
CRM Integration Tip: Most CRMs (Zoho, LeadSquared, Tally, etc.) let you configure a webhook URL. Paste the quick-send URL with your API key and map form fields to the 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.

Permission required: send_template or *

Request Body

ParameterTypeRequiredDescription
phonestringRequiredCustomer's phone number (see Phone Format)
template_namestringRequiredMeta template name (e.g., order_confirmation)
languagestringOptionalTemplate language code. Default: en
body_paramsarrayOptionalValues for body variables {{1}}, {{2}}, etc.
header_paramsarrayOptionalValues 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 Response (200)
{
  "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."
}
Error Response (400)
{
  "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.

24-hour window: If the window is closed, the API returns 400 with error_code: WINDOW_CLOSED. Use Send Template for re-engagement.
Plans: Available on both Growth (150/day) and Pro (5,000/day).
Permission required: send_text or *

Request Body

ParameterTypeRequiredDescription
phonestringRequiredCustomer's phone number
messagestringRequiredMessage 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 Response (200)
{
  "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."
}
Window Closed (400)
{
  "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.

24-hour window: If the window is closed, the API returns 400 with error_code: WINDOW_CLOSED. Use a media-header template via Send Template for re-engagement.
Plans: Available on both Growth (150/day) and Pro (5,000/day).
Permission required: send_media or *

Request Body

ParameterTypeRequiredDescription
phonestringRequiredCustomer's phone number
media_typestringRequiredOne of: image, document, video, audio
media_urlstringRequiredPublicly accessible URL of the media file
captionstringOptionalCaption for image/video/document
filenamestringOptionalFilename 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 Response (200)
{
  "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.

Plans: Available on both Growth (150/day) and Pro (5,000/day).
Permission required: send_otp or *

Request Body

ParameterTypeRequiredDescription
phonestringRequiredCustomer's phone number
template_namestringRequiredYour approved OTP template name
otpstringOptionalCustom OTP code. Auto-generated if not provided.
languagestringOptionalTemplate 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 Response (200)
{
  "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."
}
Security: The OTP is returned in the response so your system can verify it later. Do not expose this response to end users.

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 Response (200)
{
  "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 Response (200)
{
  "success": true,
  "message_id": "wamid.HBgMOTE5ODc2NTQzMjEw...",
  "api_status": "success",
  "delivery_status": "delivered",
  "sent_at": "2026-04-14T10:30:45.000Z",
  "to": "919876543210"
}
Failed Delivery (200)
{
  "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."
}
Not Found (404)
{
  "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:

FieldValuesMeaning
api_statussuccess / failedOur side — whether we accepted the send and forwarded it to Meta.
delivery_statussent / delivered / read / failedMeta side — updated from Meta's delivery webhooks.

Possible delivery_status values:

StatusMeaning
sentMessage handed to Meta, awaiting delivery.
deliveredMessage delivered to the recipient's phone (one grey tick → two grey ticks).
readRecipient has opened the message (blue ticks).
failedDelivery failed. failure_reason explains why.
Always poll this endpoint after a send. 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:

InputNormalized To
9876543210919876543210 (auto-adds 91 for India)
+91-9876543210919876543210
+91 98765 43210919876543210
919876543210919876543210 (no change)
+1415555123414155551234 (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:

PermissionEndpoints Allowed
send_templatePOST /send-template
send_textPOST /send-text
send_mediaPOST /send-media
send_otpPOST /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

CodeMeaning
200Success
400Bad request — invalid input, missing required fields, or WhatsApp not connected
401Unauthorized — missing, invalid, or expired API key
403Forbidden — API key lacks required permission
404Not found — message or resource does not exist
429Rate limit exceeded
500Internal server error

Need help? Contact us at support@twabot.com

Also see: Webhooks Documentation →