Overview

Welcome to the Botat API! This guide provides everything you need to integrate your applications with our powerful messaging platform. Whether you're a seasoned developer or just starting, you'll find clear examples and comprehensive guides to get you up and running quickly.

The Botat API provides comprehensive messaging capabilities for the WhatsApp channel. Key features include:

Versatile Messaging
Send regular text, media, and pre-approved template messages for marketing, utility, and authentication.
Conversation History
Easily retrieve complete conversation histories with any contact using simple query parameters.
Contact Management
Create, validate, and manage your contacts within the Botat platform directly through the API.

API Details

Base URL
https://app.botat.ai/
Test URL/Endpoint
https://app.botat.ai/api/v1/test/
API Version
1.0.0
Content-Type
application/json

Authentication

API Key Authentication

All API endpoints are protected and require a valid API key to be included in the header of every request. This key authenticates your application and links it to your Botat account.

Header
X-API-KEY
Value
your_access_token
Where to find your API key: You can get your API key from the profile settings page, in your Botat dashboard. Only the "Account Owner" API key will work.

Account Access Validation

  • The API key must belong to an account owner, not an invited team member.
  • Each request validates that the user has permission to access the specified inbox phone number.
  • Ensure you are using a primary account key to avoid INSUFFICIENT_PERMISSIONS errors.

Brute-Force Protection

To protect your account, we limit failed authentication attempts from a single IP address:

  • Production: 10 failed attempts per minute.
  • Test Mode: 20 failed attempts per minute.

Exceeding this triggers a TOO_MANY_AUTH_ATTEMPTS error.

Rate Limiting & Usage Quotas

To ensure API stability and fair usage, all requests are subject to rate limiting. We employ two types of limits: short-term rate limits to manage request frequency and long-term usage quotas based on your subscription plan.

Request Rate Limits

These limits control the number of requests you can make in short time intervals and are applied per API key.

Production Endpoints

Per Minute
100 requests
Per Hour
5,000 requests
Per Day
100,000 requests

Test Endpoints

Per Minute
20 requests
Per Hour
1000 requests
Per Day
5000 requests

Monitoring Usage via Headers

The API response headers provide real-time information about your current limits. Use these to build robust, adaptive integrations.

Track your short-term request frequency. When you exceed these limits, you'll receive a 429 Too Many Requests error.


            X-RateLimit-Limit: 100
            X-RateLimit-Remaining: 95
            X-RateLimit-Reset: 1640995200

API Endpoints

All endpoints are prefixed with the base URL: https://app.botat.ai. Each endpoint is detailed below with its purpose, request structure, and parameters.

1

Send Regular Message

Send a standard text or media message. This is typically used for replying to a contact within the 24-hour customer service window.

POST /api/v1/messages/whatsapp/send_message

Request Body


            {
              "inbox_phone_number": "+1234567890",
              "recipient_phone_number": "+1987654321",
              "message_text": "Hello, how can I help you?",
              "media_url": "https://example.com/image.jpg",
              "media_filename": "image.jpg"
            }

Request Examples


curl -X POST "https://app.botat.ai/api/v1/messages/whatsapp/send_message" \
  -H "X-API-KEY: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inbox_phone_number": "+1234567890",
    "recipient_phone_number": "+1987654321",
    "message_text": "Hello, how can I help you?"
  }'

const response = await fetch(
  "https://app.botat.ai/api/v1/messages/whatsapp/send_message",
  {
    method: "POST",
    headers: {
      "X-API-KEY": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      inbox_phone_number: "+1234567890",
      recipient_phone_number: "+1987654321",
      message_text: "Hello, how can I help you?",
    }),
  }
);
const result = await response.json();

import requests

response = requests.post(
    "https://app.botat.ai/api/v1/messages/whatsapp/send_message",
    headers={"X-API-KEY": "YOUR_API_KEY"},
    json={
        "inbox_phone_number": "+1234567890",
        "recipient_phone_number": "+1987654321",
        "message_text": "Hello, how can I help you?",
    },
    timeout=30,
)
print(response.json())

<?php
$ch = curl_init("https://app.botat.ai/api/v1/messages/whatsapp/send_message");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        "X-API-KEY: YOUR_API_KEY",
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "inbox_phone_number" => "+1234567890",
        "recipient_phone_number" => "+1987654321",
        "message_text" => "Hello, how can I help you?",
    ]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);

Parameters

Parameter Required Description
inbox_phone_number Yes Your Botat business phone number.
recipient_phone_number Yes The contact's phone number.
message_text Yes* Message content (max 4096 chars).
media_url No Public URL to a media file.
media_type No `image`, `video`, `audio`, `document`.
media_caption No Caption for the media file.
media_filename No Required if `media_url` is provided.
2

Send Template Message

Send a pre-approved message template. This is required to initiate conversations or message a user outside the 24-hour window.

POST /api/v1/messages/whatsapp/send_template

Request Body


            {
              "inbox_phone_number": "+1234567890",
              "recipient_phone_number": "+1987654321",
              "template_name": "welcome_message",
              "language_code": "en",
              "template_parameters": { /* ... */ }
            }

Request Examples


curl -X POST "https://app.botat.ai/api/v1/messages/whatsapp/send_template" \
  -H "X-API-KEY: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inbox_phone_number": "+1234567890",
    "recipient_phone_number": "+1987654321",
    "template_name": "welcome_message",
    "language_code": "en",
    "template_parameters": {
      "customer_name": "John Doe"
    }
  }'

const response = await fetch(
  "https://app.botat.ai/api/v1/messages/whatsapp/send_template",
  {
    method: "POST",
    headers: {
      "X-API-KEY": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      inbox_phone_number: "+1234567890",
      recipient_phone_number: "+1987654321",
      template_name: "welcome_message",
      language_code: "en",
      template_parameters: { customer_name: "John Doe" },
    }),
  }
);
const result = await response.json();

import requests

response = requests.post(
    "https://app.botat.ai/api/v1/messages/whatsapp/send_template",
    headers={"X-API-KEY": "YOUR_API_KEY"},
    json={
        "inbox_phone_number": "+1234567890",
        "recipient_phone_number": "+1987654321",
        "template_name": "welcome_message",
        "language_code": "en",
        "template_parameters": {"customer_name": "John Doe"},
    },
    timeout=30,
)
print(response.json())

<?php
$ch = curl_init("https://app.botat.ai/api/v1/messages/whatsapp/send_template");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        "X-API-KEY: YOUR_API_KEY",
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "inbox_phone_number" => "+1234567890",
        "recipient_phone_number" => "+1987654321",
        "template_name" => "welcome_message",
        "language_code" => "en",
        "template_parameters" => ["customer_name" => "John Doe"],
    ]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);

Parameters

Parameter Required Description
template_name Yes Name of your approved template.
language_code Yes Language of the template (e.g., `en`).
template_parameters No Object with values for template variables.
media_url No URL for custom media header.
OTP_code No Required for Authentication templates.
3

Get Conversation History

Retrieve the message history for a specific conversation between your inbox and a contact.

GET /api/v1/messages/whatsapp/conversation

Request Examples


curl -G "https://app.botat.ai/api/v1/messages/whatsapp/conversation" \
  -H "X-API-KEY: YOUR_API_KEY" \
  --data-urlencode "inbox_phone_number=+1234567890" \
  --data-urlencode "contact_phone_number=+1987654321" \
  --data-urlencode "limit=20"

const params = new URLSearchParams({
  inbox_phone_number: "+1234567890",
  contact_phone_number: "+1987654321",
  limit: "20",
});

const response = await fetch(
  `https://app.botat.ai/api/v1/messages/whatsapp/conversation?${params}`,
  { headers: { "X-API-KEY": "YOUR_API_KEY" } }
);
const result = await response.json();

import requests

response = requests.get(
    "https://app.botat.ai/api/v1/messages/whatsapp/conversation",
    headers={"X-API-KEY": "YOUR_API_KEY"},
    params={
        "inbox_phone_number": "+1234567890",
        "contact_phone_number": "+1987654321",
        "limit": 20,
    },
    timeout=30,
)
print(response.json())

<?php
$query = http_build_query([
    "inbox_phone_number" => "+1234567890",
    "contact_phone_number" => "+1987654321",
    "limit" => 20,
]);

$ch = curl_init(
    "https://app.botat.ai/api/v1/messages/whatsapp/conversation?" . $query
);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["X-API-KEY: YOUR_API_KEY"],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);

Query Parameters

Parameter Required Description
inbox_phone_number Yes Your Botat business phone number.
contact_phone_number Yes The contact's phone number.
limit No Messages per page (Default: 20, Max: 100).
offset No Messages to skip for pagination.
after_date No ISO 8601 date filter.
before_date No ISO 8601 date filter.
4

List Templates

Fetch message templates for your account. The endpoint returns approved templates by default and can be filtered by status to inspect pending or rejected templates.

GET /api/v1/whatsapp/templates

Request Examples


curl -G "https://app.botat.ai/api/v1/whatsapp/templates" \
  -H "X-API-KEY: YOUR_API_KEY" \
  --data-urlencode "inbox_phone_number=+1234567890" \
  --data-urlencode "status=APPROVED"

const params = new URLSearchParams({
  inbox_phone_number: "+1234567890",
  status: "APPROVED",
});

const response = await fetch(
  `https://app.botat.ai/api/v1/whatsapp/templates?${params}`,
  { headers: { "X-API-KEY": "YOUR_API_KEY" } }
);
const result = await response.json();

import requests

response = requests.get(
    "https://app.botat.ai/api/v1/whatsapp/templates",
    headers={"X-API-KEY": "YOUR_API_KEY"},
    params={
        "inbox_phone_number": "+1234567890",
        "status": "APPROVED",
    },
    timeout=30,
)
print(response.json())

<?php
$query = http_build_query([
    "inbox_phone_number" => "+1234567890",
    "status" => "APPROVED",
]);

$ch = curl_init("https://app.botat.ai/api/v1/whatsapp/templates?" . $query);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["X-API-KEY: YOUR_API_KEY"],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);

Query Parameters

Parameter Required Description
inbox_phone_number Yes Your Botat business phone number.
category No Filter by `MARKETING`, `UTILITY`, etc.
language No Filter by language code (e.g., `en_US`).
status No Filter by status (Default: `APPROVED`).
5

Create Contact

Create or validate a contact in your Botat account. This is useful for ensuring a number is valid before sending a message.

POST /api/v1/contacts/whatsapp/create

Request Body


            {
              "phone_number": "+1987654321",
              "name": "John Doe"
            }

Request Examples


curl -X POST "https://app.botat.ai/api/v1/contacts/whatsapp/create" \
  -H "X-API-KEY: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "+1987654321",
    "name": "John Doe"
  }'

const response = await fetch(
  "https://app.botat.ai/api/v1/contacts/whatsapp/create",
  {
    method: "POST",
    headers: {
      "X-API-KEY": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      phone_number: "+1987654321",
      name: "John Doe",
    }),
  }
);
const result = await response.json();

import requests

response = requests.post(
    "https://app.botat.ai/api/v1/contacts/whatsapp/create",
    headers={"X-API-KEY": "YOUR_API_KEY"},
    json={
        "phone_number": "+1987654321",
        "name": "John Doe",
    },
    timeout=30,
)
print(response.json())

<?php
$ch = curl_init("https://app.botat.ai/api/v1/contacts/whatsapp/create");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        "X-API-KEY: YOUR_API_KEY",
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "phone_number" => "+1987654321",
        "name" => "John Doe",
    ]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);

Parameters

Parameter Required Description
phone_number Yes The contact's phone number.
name No The display name for the contact.

Guide: Creating WhatsApp Templates

Create WhatsApp message templates and submit them to Meta for review. You can create text-only templates with JSON, or upload a media header and create the template in one multipart request.

Recommended: send your request to the test endpoint first. It performs the same local validation without uploading media or submitting a template to Meta.

Endpoints

Purpose Endpoint
Create template POST /api/v1/whatsapp/templates
Validate creation request POST /api/v1/test/whatsapp/templates
Upload header media separately POST /api/v1/whatsapp/templates/media
Validate a media upload POST /api/v1/test/whatsapp/templates/media

All endpoints require X-API-KEY. The requested inbox_phone_number must belong to the authenticated Botat account.

6

Create Template

Create a WhatsApp message template and submit it to Meta for review. Send plain JSON for templates that do not need an image, video, or document header.

POST /api/v1/whatsapp/templates

To validate the same request without submitting anything to Meta, send it to /api/v1/test/whatsapp/templates instead.

The template fields may also be nested under a template object for compatibility with the Botat dashboard payload.

Request Body


            {
              "inbox_phone_number": "+966500000000",
              "name": "order_ready",
              "category": "UTILITY",
              "language": "en_US",
              "components": [ /* Meta template components */ ]
            }

Request Examples


curl --fail-with-body \
  -X POST "https://app.botat.ai/api/v1/whatsapp/templates" \
  -H "X-API-KEY: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inbox_phone_number": "+966500000000",
    "name": "order_ready",
    "category": "UTILITY",
    "language": "en_US",
    "components": [
      {
        "type": "BODY",
        "text": "Hello {{1}}, your order {{2}} is ready for collection today.",
        "example": {
          "body_text": [["Ahmed", "1234"]]
        }
      }
    ]
  }'

const response = await fetch(
  "https://app.botat.ai/api/v1/whatsapp/templates",
  {
    method: "POST",
    headers: {
      "X-API-KEY": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      inbox_phone_number: "+966500000000",
      name: "order_ready",
      category: "UTILITY",
      language: "en_US",
      components: [
        {
          type: "BODY",
          text: "Hello {{1}}, your order {{2}} is ready for collection today.",
          example: { body_text: [["Ahmed", "1234"]] },
        },
      ],
    }),
  }
);
const result = await response.json();

import requests

response = requests.post(
    "https://app.botat.ai/api/v1/whatsapp/templates",
    headers={"X-API-KEY": "YOUR_API_KEY"},
    json={
        "inbox_phone_number": "+966500000000",
        "name": "order_ready",
        "category": "UTILITY",
        "language": "en_US",
        "components": [
            {
                "type": "BODY",
                "text": "Hello {{1}}, your order {{2}} is ready for collection today.",
                "example": {"body_text": [["Ahmed", "1234"]]},
            }
        ],
    },
    timeout=30,
)
print(response.json())

<?php
$ch = curl_init("https://app.botat.ai/api/v1/whatsapp/templates");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        "X-API-KEY: YOUR_API_KEY",
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "inbox_phone_number" => "+966500000000",
        "name" => "order_ready",
        "category" => "UTILITY",
        "language" => "en_US",
        "components" => [
            [
                "type" => "BODY",
                "text" => "Hello {{1}}, your order {{2}} is ready for collection today.",
                "example" => ["body_text" => [["Ahmed", "1234"]]],
            ],
        ],
    ]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);

Parameters

Parameter Required Description
inbox_phone_number Yes A WhatsApp business number owned by the authenticated account.
name Yes Lowercase letters, numbers, and underscores. Must start with a letter.
category Yes MARKETING, UTILITY, or AUTHENTICATION.
language Yes A Meta language code such as en_US or ar.
components Yes A non-empty array of Meta template components.
7

Create Template with Media

The recommended media flow, sent as a single multipart/form-data request. Botat validates the template and file, uploads the media to Meta, adds the correct header component, and submits the template.

POST /api/v1/whatsapp/templates
Important: in the cURL example, the @ before the local path is required. Without it, curl sends the path as text and the API returns FILE_NOT_UPLOADED. In browsers, use FormData and never set the Content-Type header manually. Do not add the media HEADER component yourself in this one-request flow.

Supported Header Media

Header type Accepted MIME types Maximum size
Document application/pdf 10MB
Image image/jpeg, image/png 5MB
Video video/mp4 12MB

Botat verifies both the declared MIME type and the file's binary signature.

Use exactly one file field per request. In the cURL example, replace the PDF line with the matching line for your media (other languages only need the matching filename and MIME type):


# JPEG
-F "file=@./header.jpg;type=image/jpeg"

# PNG
-F "file=@./header.png;type=image/png"

# MP4
-F "file=@./header.mp4;type=video/mp4"

Request Examples


curl --fail-with-body \
  -X POST "https://app.botat.ai/api/v1/whatsapp/templates" \
  -H "X-API-KEY: YOUR_API_KEY" \
  -F "inbox_phone_number=+966500000000" \
  -F 'template={
    "name": "account_document_ready",
    "category": "UTILITY",
    "language": "en_US",
    "components": [
      {
        "type": "BODY",
        "text": "Your requested account document is ready. Use the button below for more information."
      },
      {
        "type": "BUTTONS",
        "buttons": [
          {
            "type": "URL",
            "text": "Visit Botat",
            "url": "https://botat.ai"
          }
        ]
      }
    ]
  }' \
  -F "file=@./account-document.pdf;type=application/pdf"

// In browsers, take the file from a file input. Do not set the
// Content-Type header manually: the browser must add the
// multipart boundary itself.
const fileInput = document.querySelector('input[type="file"]');

const formData = new FormData();
formData.append("inbox_phone_number", "+966500000000");
formData.append(
  "template",
  JSON.stringify({
    name: "account_document_ready",
    category: "UTILITY",
    language: "en_US",
    components: [
      {
        type: "BODY",
        text: "Your requested account document is ready. Use the button below for more information.",
      },
      {
        type: "BUTTONS",
        buttons: [
          { type: "URL", text: "Visit Botat", url: "https://botat.ai" },
        ],
      },
    ],
  })
);
formData.append("file", fileInput.files[0]);

const response = await fetch(
  "https://app.botat.ai/api/v1/whatsapp/templates",
  {
    method: "POST",
    headers: { "X-API-KEY": "YOUR_API_KEY" },
    body: formData,
  }
);

const result = await response.json();

import json
import requests

template = {
    "name": "account_document_ready",
    "category": "UTILITY",
    "language": "en_US",
    "components": [
        {
            "type": "BODY",
            "text": "Your requested account document is ready. Use the button below for more information.",
        },
        {
            "type": "BUTTONS",
            "buttons": [
                {"type": "URL", "text": "Visit Botat", "url": "https://botat.ai"}
            ],
        },
    ],
}

with open("account-document.pdf", "rb") as file:
    response = requests.post(
        "https://app.botat.ai/api/v1/whatsapp/templates",
        headers={"X-API-KEY": "YOUR_API_KEY"},
        data={
            "inbox_phone_number": "+966500000000",
            "template": json.dumps(template),
        },
        files={"file": ("account-document.pdf", file, "application/pdf")},
        timeout=60,
    )
print(response.json())

<?php
$template = json_encode([
    "name" => "account_document_ready",
    "category" => "UTILITY",
    "language" => "en_US",
    "components" => [
        [
            "type" => "BODY",
            "text" => "Your requested account document is ready. Use the button below for more information.",
        ],
        [
            "type" => "BUTTONS",
            "buttons" => [
                ["type" => "URL", "text" => "Visit Botat", "url" => "https://botat.ai"],
            ],
        ],
    ],
]);

$ch = curl_init("https://app.botat.ai/api/v1/whatsapp/templates");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ["X-API-KEY: YOUR_API_KEY"],
    CURLOPT_POSTFIELDS => [
        "inbox_phone_number" => "+966500000000",
        "template" => $template,
        "file" => new CURLFile("account-document.pdf", "application/pdf"),
    ],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);

Parameters

Parameter Required Description
inbox_phone_number Yes A WhatsApp business number owned by the authenticated account.
template Yes The template JSON as a string. Same fields as Create Template, without the media HEADER component.
file Yes The header media file. See Supported Header Media.
8

Upload Header Media Separately

Use the optional two-step flow when your integration needs direct control over the Meta upload handle. Upload the file here first, then create the template with Create Template.

POST /api/v1/whatsapp/templates/media

To validate the file without uploading it to Meta, send the same request to /api/v1/test/whatsapp/templates/media instead.

Request Examples


curl --fail-with-body \
  -X POST "https://app.botat.ai/api/v1/whatsapp/templates/media" \
  -H "X-API-KEY: YOUR_API_KEY" \
  -F "inbox_phone_number=+966500000000" \
  -F "file=@./account-document.pdf;type=application/pdf"

const fileInput = document.querySelector('input[type="file"]');

const formData = new FormData();
formData.append("inbox_phone_number", "+966500000000");
formData.append("file", fileInput.files[0]);

const response = await fetch(
  "https://app.botat.ai/api/v1/whatsapp/templates/media",
  {
    method: "POST",
    headers: { "X-API-KEY": "YOUR_API_KEY" },
    body: formData,
  }
);
const result = await response.json();

import requests

with open("account-document.pdf", "rb") as file:
    response = requests.post(
        "https://app.botat.ai/api/v1/whatsapp/templates/media",
        headers={"X-API-KEY": "YOUR_API_KEY"},
        data={"inbox_phone_number": "+966500000000"},
        files={"file": ("account-document.pdf", file, "application/pdf")},
        timeout=60,
    )
print(response.json())

<?php
$ch = curl_init("https://app.botat.ai/api/v1/whatsapp/templates/media");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ["X-API-KEY: YOUR_API_KEY"],
    CURLOPT_POSTFIELDS => [
        "inbox_phone_number" => "+966500000000",
        "file" => new CURLFile("account-document.pdf", "application/pdf"),
    ],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);

Parameters

Parameter Required Description
inbox_phone_number Yes A WhatsApp business number owned by the authenticated account.
file Yes The header media file. See Supported Header Media above.

Success Response

Copy the returned data.component into the template's components array. The test upload endpoint validates the file but returns handle: null because it does not upload anything to Meta.


{
  "success": true,
  "api_version": "1.0.0",
  "data": {
    "handle": "META_UPLOAD_HANDLE",
    "h": "META_UPLOAD_HANDLE",
    "header_handle": ["META_UPLOAD_HANDLE"],
    "media_type": "DOCUMENT",
    "mime_type": "application/pdf",
    "file_name": "account-document.pdf",
    "file_size": 12345,
    "component": {
      "type": "HEADER",
      "format": "DOCUMENT",
      "example": {
        "header_handle": ["META_UPLOAD_HANDLE"]
      }
    }
  },
  "message": "Template media uploaded successfully"
}

Validate Before Submission

Replace /api/v1/ with /api/v1/test/ and send the same JSON or multipart request. Test mode checks authentication, inbox ownership, the template, and any uploaded file, but does not call Meta.

Validation errors identify the exact field and suggest how to fix it:


{
  "success": false,
  "api_version": "1.0.0",
  "error": {
    "code": "TEMPLATE_VALIDATION_FAILED",
    "message": "The WhatsApp template payload is invalid",
    "details": "1 validation error(s) found. Fix validation_errors and retry.",
    "validation_errors": [
      {
        "field": "components[0].text",
        "code": "PARAMETERS_WORD_RATIO_EXCEEDED",
        "message": "Body has 5 non-variable word(s) for 3 variable(s); Meta requires at least 7.",
        "suggestion": "Add at least 2 descriptive non-variable word(s), or remove variables."
      }
    ],
    "template_help": {
      "description": "Use sequential numbered variables, realistic examples, and at least 2P + 1 non-variable body words for P variables."
    }
  }
}
  • Numbered variables must be sequential and have matching, non-empty examples.
  • Named variables require parameter_format: "NAMED" and matching named examples.
  • A body with P variables needs at least 2P + 1 non-variable words.
  • Header, footer, buttons, authentication, OTP, and TTL rules are checked before submission.

Success Response

Production returns HTTP 201 when Meta accepts the template for review.


{
  "success": true,
  "api_version": "1.0.0",
  "data": {
    "template_id": "123456789",
    "template_name": "order_ready",
    "template_category": "UTILITY",
    "language": "en_US",
    "status": "PENDING",
    "submitted_at": "2026-08-04T12:00:00.000Z",
    "meta_response_details": {
      "id": "123456789",
      "status": "PENDING",
      "category": "UTILITY"
    }
  },
  "message": "WhatsApp template submitted successfully"
}
A successful creation response means Meta accepted the template for review, not that it is approved. Use GET /api/v1/whatsapp/templates?inbox_phone_number=...&status=PENDING to check its current status.

Guide: Sending Template Messages

This guide will walk you through the process of sending pre-approved WhatsApp Business templates, from constructing the basic request to handling complex parameter types and errors.

1 Endpoint & Basic Request

All template messages are sent using a POST request to the following endpoint. Start with this basic structure and modify it based on your template's needs.

POST /api/v1/messages/whatsapp/send_template
{
              "inbox_phone_number": "+1234567890",
              "recipient_phone_number": "+1987654321",
              "template_name": "your_template_name",
              "language_code": "en",
              "template_parameters": { /* ... */ }
            }

2 Constructing Template Parameters

The structure of the template_parameters object is critical and depends entirely on your template's design. Select a tab below to see the correct structure for each use case.

For templates with variables like {{customer_name}}.


            {
              "template_parameters": {
                "customer_name": "John Doe",
                "platform_name": "Our Platform"
              }
            }

3 Troubleshooting Common Errors

If your request fails, the API will return a descriptive error. The most common issue is a parameter mismatch, for which the API provides a helpful guide.

Template Parameter Validation Error

This error includes a template_help object with a correct example to guide you.


            {
              "success": false,
              "error": {
                "code": "TEMPLATE_VALIDATION_FAILED",
                "message": "Template parameters are missing or invalid",
                "template_help": {
                  "type": "named_parameters",
                  "example_request": {
                    "template_parameters": {
                      "customer_name": "your_customer_name_value"
                    }
                  }
                }
              }
            }

4 Quick Reference & Best Practices

Parameter Rules

  • Named vars go directly in template_parameters.
  • Numbered vars are grouped by component (body, header).
  • Button vars use indexed keys ("0", "1") in a button object.

Best Practices

  • Test first: Always use the test endpoint to validate requests.
  • Handle limits: Implement exponential backoff for rate limit errors.
  • Check templates: Use the "List Templates" endpoint to see parameter requirements before sending.
Pro Tip: Before sending a template, call the `GET /api/v1/whatsapp/templates` endpoint. This will show you the exact structure of your approved templates, including the parameter names and types, which will help you build your request correctly on the first try.

Request/Response Structures

Standard Response Format

All API responses, whether successful or not, follow a consistent JSON structure.


{
  "success": true,
  "api_version": "1.0.0",
  "test_mode": false,
  "data": { /* Endpoint-specific data object */ },
  "message": "A human-readable success message"
}

Error Response Format

When a request fails, success will be false and an error object will be included with specific details.


{
  "success": false,
  "api_version": "1.0.0",
  "test_mode": false,
  "error": {
    "code": "ERROR_CODE_IDENTIFIER",
    "message": "A human-readable error message explaining the issue.",
    "details": "Additional technical details about the error.",
    "validation_errors": [
      "The recipient_phone_number field is required."
    ],
    "template_help": { /* Included for template validation errors */ }
  }
}

Template Parameter Types

Message templates can use different types of variables (parameters). The Botat API supports them all, and it's important to structure your template_parameters object correctly.

1. Named Parameters

Used for variables like {{customer_name}}. Place them directly in the root of the template_parameters object.


"template_parameters": {
  "customer_name": "John Doe",
  "platform_name": "Our Platform"
}

2. Numbered Parameters

Used for traditional variables like {{1}}. These must be nested inside a body object.


"template_parameters": {
  "body": {
    "1": "John Doe",
    "2": "ORD-12345"
  }
}

3. Button Parameters

Parameters for dynamic URLs in buttons are always indexed (starting from 0) and nested inside a button object.


"template_parameters": {
  "button": {
    "0": "https://example.com/website1",
    "1": "https://example.com/website2"
  }
}

4. Mixed Parameters

If a template uses a combination of parameter types (e.g., named in the header, numbered in the body), combine the structures.


"template_parameters": {
  "customer_name": "John Doe",
  "body": {
    "1": "Premium Plan",
    "2": "January 31, 2025"
  },
  "button": {
    "0": "https://example.com/renew",
    "1": "https://example.com/upgrade"
  }
}

5. Authentication (OTP) Templates

For AUTHENTICATION templates, simply provide the OTP_code. The API automatically formats it for the body and button components.


"template_parameters": {
  "OTP_code": "123456"
}

Template Validation Help

If you structure the parameters incorrectly, the API will return a helpful template_help object in the error response, guiding you on the correct structure for that specific template.


{
  "error": {
    "code": "TEMPLATE_VALIDATION_FAILED",
    "template_help": {
      "type": "mixed_parameters",
      "description": "This template uses mixed parameter types...",
      "example_request": {
        "template_parameters": {
          "customer_name": "your_customer_name_value",
          "body": { "1": "value_for_{{1}}", "2": "value_for_{{2}}" },
          "button": { "0": "url_for_button_0", "1": "url_for_button_1" }
        }
      }
    }
  }
}

Error Codes Reference

Our API uses standard HTTP status codes to indicate the success or failure of a request. Errors are returned with a consistent JSON body containing a specific code and a helpful message. Below is a reference of common errors grouped by HTTP status code.

Validation Rules & Requirements

To ensure data integrity and security, all incoming requests are rigorously validated. Understanding these rules is key to successful integration.

Phone Number Format

  • Must be in international format, starting with a + followed by the country code (e.g., +1234567890).
  • Spaces, parentheses, and dashes are automatically removed (e.g., +1 (234) 567-8900 is valid).
  • Must contain 8-15 digits after the country code.

Content Length Limits

Field Max Length
message_text 4096 characters
template_name 256 characters
Template parameter value 1024 characters
media_caption 1024 characters

Message Media Requirements

Maximum File Size
5MB per file.
Supported Images
JPEG, PNG, GIF, WebP.
Supported Videos
MP4, 3GPP, QuickTime, AVI, MKV.
Supported Audio
AAC, MP3, OGG, WAV, Opus.
Supported Documents
PDF, ZIP, DOC/DOCX, TXT.

These limits apply to message media URLs. Template header uploads use the type-specific limits in the Create Template Guide.

Media Validation Process

When you provide a media_url, we perform a series of checks to ensure the file is valid and secure before sending it.

  • 1

    URL Validation & SSRF Protection

  • 2

    File Accessibility & Size Check

  • 3

    Magic Number Detection

  • 4

    Content-Type Matching

Security Measures

Security is a top priority at Botat. We've implemented a multi-layered security strategy to protect your data, our platform, and your customers. Here are the key measures in place.

Network Security

SSRF Protection
We prevent Server-Side Request Forgery by blocking all media_url requests to private and reserved IP networks, ensuring we only fetch from public sources.
Request Timeouts
API requests automatically time out after 30 seconds to protect against slow loris attacks and ensure system stability.

Application Security

Secure Headers
All API responses include security headers like X-Frame-Options and X-Content-Type-Options to protect your application from common web vulnerabilities.
Payload Size Limits
Multipart fields are limited to 1MB. Template header files are limited by type, up to 12MB, preventing resource exhaustion from oversized uploads.
Early Authentication
API-key authentication and rate limiting run before multipart files are buffered. Inbox ownership is verified before media is uploaded or a template is submitted.

Data Protection

Phone Number Masking
To protect privacy, phone numbers in API responses and logs are always partially masked (e.g., +12***890).
Input Sanitization
All user-provided inputs are sanitized before logging to prevent log injection vulnerabilities.
Secure File Handling
We use magic number detection to verify file types, preventing content-type spoofing and malicious file uploads.

Testing & Development

A robust testing strategy is essential for a successful integration. We provide a complete sandbox environment that allows you to validate every aspect of your code without sending real messages or incurring charges.

Sandbox Environment

Every production endpoint has a corresponding test endpoint. To use it, simply insert /test into the URL path. You can also try both modes interactively with the Try It console shown next to every endpoint in this documentation.

Production Endpoint
/api/v1/messages/whatsapp/send_template
Test Endpoint
/api/v1/test/messages/whatsapp/send_template
Create Template
/api/v1/whatsapp/templates
Validate Template Creation
/api/v1/test/whatsapp/templates

For template creation, test mode performs full local template and media validation but never uploads the file or submits the template to Meta.

Validate Requests

Check your request structure, authentication, and parameters without sending a real message.

No Costs or Side-Effects

Avoid incurring charges from the underlying provider (Meta) and prevent sending accidental test messages to real customers.

Higher Rate Limits

The sandbox environment offers higher rate limits, allowing for more rapid development and integration testing.

Simulated Responses

Test mode returns a simulated success or failure, including the `test_mode: true` flag, allowing you to debug your integration safely.

Development Checklist

Follow these best practices to ensure a smooth and reliable integration.

  • Use Test Endpoints First: Always validate your requests against the sandbox before deploying to production.
  • Handle Rate Limits Gracefully: Implement exponential backoff when you receive a `429` error to avoid being blocked.
  • Parse Error Details: Don't just check for success/failure. Use the `code` and `template_help` objects in error responses to debug effectively.
  • Secure Media URLs: Ensure all `media_url` links are public, directly accessible, and use HTTPS.
  • Monitor Usage Headers: Keep an eye on `X-API-Requests-Remaining` to stay within your monthly plan limits.

Support & Resources

You've reached the end of the guide! Here you'll find a summary of key information and best practices to ensure your integration is a success.

Need Assistance?

The most effective way to get help is to review the API's error responses. They are designed to be self-explanatory and often include a template_help object or detailed message to guide you. If you're still stuck, our support team is ready to help!

HTTP Status Codes

A quick reference for the primary HTTP status codes our API uses.

200 OK Request successful.
400 Bad Request Your request has a validation error.
401 Unauthorized Your API key is missing or invalid.
403 Forbidden You don't have permission for this action.
404 Not Found The requested resource does not exist.
429 Too Many Requests You've hit a rate limit.
500 Server Error An unexpected error occurred on our side.

Response Time Expectations

Typical response times for our API endpoints.

Regular & Template Messages
< 3 seconds
Media Messages
5-10 seconds (depending on file size)
Listing & History Endpoints
< 2 seconds

Final Best Practices

Monitor Headers

Actively monitor the X-RateLimit-Remaining and X-API-Requests-Remaining headers to avoid hitting limits unexpectedly.

Cache When Possible

Cache the response from the "List Templates" endpoint. Your available templates won't change frequently, and this will reduce redundant API calls.

You now have all the tools to build amazing things with the Botat API.

Happy Building!