GhalaGhalaHelp Center
Back to ghala.ioghala.ioSign in
  • Getting Started
    • Ghala Documentation
    • Send Your First WhatsApp Message via API
    • Setting Up WhatsApp Webhooks
  • Commerce
    • Start Selling on WhatsApp
    • Set Up the AI Sales Agent
    • Get Paid with Snippe
  • Ai Automation
    • Configuring AI Auto-Reply for WhatsApp
    • Setting Up Human Handover Protocol
  • Campaigns Messaging
    • WhatsApp Message Templates: Complete Guide
    • Bulk WhatsApp Messaging: Complete Campaign Guide
  • Contacts Crm
    • WhatsApp Contact Management Guide
  • Best Practices
    • WhatsApp Customer Support Best Practices
    • Message Template Best Practices
  • Api Reference
    • Ghala API Reference

Products

  • Ghala
  • Sarufi
  • Snippe
  • Sema

Explore

  • Use Cases
  • Pricing
  • Blog

Developers

  • Docs
  • API Reference
  • API Quickstart
  • Webhooks Guide

Contact

  • SkyCity Mall, 9th Floor, Dar es Salaam, Tanzania
  • info@ghala.io
  • +255 699 920 009
© 2026 Neurotech Company LimitedTerms of ServicePrivacy PolicySitemap
  1. Help Center
  2. Api Reference
  3. Ghala API Reference

Ghala API Reference

Complete API documentation for Ghala. Authentication, endpoints, examples, and error handling.

Overview

The Ghala API is plain HTTPS + JSON on top of the official WhatsApp Cloud API. With it you can:

  • Send WhatsApp messages: text, templates, and media
  • Manage contacts, tags, and custom fields
  • Create and submit message templates
  • Receive events on your own endpoint via webhooks
  • Pull message analytics

Base URL:

https://api.ghala.io/api/v1

Request examples come in cURL, Python, and JavaScript; pick your language once and every example on the page follows. Every block has a copy button, and Copy for AI (top of the page) copies this whole reference as markdown, ready to paste into your AI assistant. No SDK required.

Authentication

All requests require an API key in the Authorization header:

Authorization: Bearer YOUR_API_KEY

Create and rotate keys in Dashboard → Developer → Credentials.

  • Keep keys out of code and git; use environment variables
  • Use separate keys for development and production
  • Rotate any key you suspect has leaked; revocation is immediate

Response envelope

Every response shares one shape:

{ "success": true, "data": { } }

List endpoints paginate with page and limit query parameters and return:

{
  "success": true,
  "data": [ ],
  "pagination": { "page": 1, "limit": 20, "total": 143 }
}

Rate limits

Tier Requests/min
Starter 60
Growth 300
Enterprise 1000+

Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers. On 429, back off until the reset timestamp; don't retry in a tight loop.

The 24-hour session window

WhatsApp only allows free-form messages within 24 hours of the customer's last inbound message. Outside that window, sends fail with OUTSIDE_SESSION_WINDOW; use a template message instead. This is a WhatsApp platform rule, not a Ghala limit, and it's the most common first-integration surprise.

Messages

Send a text message

POST /messages

curl -X POST https://api.ghala.io/api/v1/messages \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "255712345678",
    "type": "text",
    "text": { "body": "Hello from Ghala!" }
  }'
import requests

resp = requests.post(
    "https://api.ghala.io/api/v1/messages",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "to": "255712345678",
        "type": "text",
        "text": {"body": "Hello from Ghala!"},
    },
)
print(resp.json())
const resp = await fetch("https://api.ghala.io/api/v1/messages", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    to: "255712345678",
    type: "text",
    text: { body: "Hello from Ghala!" },
  }),
});
console.log(await resp.json());

Response:

{
  "success": true,
  "data": { "message_id": "wamid.HBgM...", "status": "sent" }
}

to is full international format without +. sent means WhatsApp accepted the message; delivery and read receipts arrive later as webhook events.

Send a template message

POST /messages

curl -X POST https://api.ghala.io/api/v1/messages \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "255712345678",
    "type": "template",
    "template": {
      "name": "order_update",
      "language": { "code": "en" },
      "components": [
        {
          "type": "body",
          "parameters": [
            { "type": "text", "text": "John" },
            { "type": "text", "text": "1042" }
          ]
        }
      ]
    }
  }'
import requests

resp = requests.post(
    "https://api.ghala.io/api/v1/messages",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "to": "255712345678",
        "type": "template",
        "template": {
            "name": "order_update",
            "language": {"code": "en"},
            "components": [
                {
                    "type": "body",
                    "parameters": [
                        {"type": "text", "text": "John"},
                        {"type": "text", "text": "1042"},
                    ],
                }
            ],
        },
    },
)
print(resp.json())
const resp = await fetch("https://api.ghala.io/api/v1/messages", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    to: "255712345678",
    type: "template",
    template: {
      name: "order_update",
      language: { code: "en" },
      components: [
        {
          type: "body",
          parameters: [
            { type: "text", text: "John" },
            { type: "text", text: "1042" },
          ],
        },
      ],
    },
  }),
});
console.log(await resp.json());

Parameters fill the template's {{1}}, {{2}} placeholders in order. The template must be approved and the language.code must match one of its submitted languages.

Send a media message

POST /messages

curl -X POST https://api.ghala.io/api/v1/messages \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "255712345678",
    "type": "image",
    "image": {
      "url": "https://example.com/image.jpg",
      "caption": "Check out our new product!"
    }
  }'
import requests

resp = requests.post(
    "https://api.ghala.io/api/v1/messages",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "to": "255712345678",
        "type": "image",
        "image": {
            "url": "https://example.com/image.jpg",
            "caption": "Check out our new product!",
        },
    },
)
print(resp.json())
const resp = await fetch("https://api.ghala.io/api/v1/messages", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    to: "255712345678",
    type: "image",
    image: {
      url: "https://example.com/image.jpg",
      caption: "Check out our new product!",
    },
  }),
});
console.log(await resp.json());

type can be image, video, audio, or document with a matching object. Media URLs must be public HTTPS; documents also accept a filename.

Message statuses

Status Meaning
sent Accepted by WhatsApp
delivered Reached the recipient's device
read Opened by the recipient
failed Not delivered; the webhook event includes the reason

Contacts

List contacts

GET /contacts

Query parameters: page, limit (max 100), tag, search (name or phone).

Get / update / delete a contact

GET /contacts/{phone} · PUT /contacts/{phone} · DELETE /contacts/{phone}

Create a contact

POST /contacts

curl -X POST https://api.ghala.io/api/v1/contacts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "255712345678",
    "name": "John Doe",
    "email": "john@example.com",
    "tags": ["customer", "vip"],
    "custom_fields": { "company": "Acme Inc" }
  }'
import requests

resp = requests.post(
    "https://api.ghala.io/api/v1/contacts",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "phone": "255712345678",
        "name": "John Doe",
        "email": "john@example.com",
        "tags": ["customer", "vip"],
        "custom_fields": {"company": "Acme Inc"},
    },
)
print(resp.json())
const resp = await fetch("https://api.ghala.io/api/v1/contacts", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    phone: "255712345678",
    name: "John Doe",
    email: "john@example.com",
    tags: ["customer", "vip"],
    custom_fields: { company: "Acme Inc" },
  }),
});
console.log(await resp.json());

Tags and custom fields are the raw material for campaign segments; keep them consistent.

Templates

List / get templates

GET /templates · GET /templates/{name}

Each template includes its Meta review status: PENDING, APPROVED, or REJECTED. Only approved templates can be sent.

Create a template

POST /templates

curl -X POST https://api.ghala.io/api/v1/templates \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "order_confirmation",
    "category": "UTILITY",
    "language": "en",
    "components": [
      { "type": "BODY", "text": "Hi {{1}}, your order #{{2}} is confirmed!" }
    ]
  }'
import requests

resp = requests.post(
    "https://api.ghala.io/api/v1/templates",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "name": "order_confirmation",
        "category": "UTILITY",
        "language": "en",
        "components": [
            {"type": "BODY", "text": "Hi {{1}}, your order #{{2}} is confirmed!"}
        ],
    },
)
print(resp.json())
const resp = await fetch("https://api.ghala.io/api/v1/templates", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "order_confirmation",
    category: "UTILITY",
    language: "en",
    components: [
      { type: "BODY", text: "Hi {{1}}, your order #{{2}} is confirmed!" },
    ],
  }),
});
console.log(await resp.json());
  • name: lowercase letters, numbers, and underscores only
  • category: UTILITY (transactional), MARKETING, or AUTHENTICATION; Meta reviews against the category, and miscategorized templates get rejected
  • Approval usually takes minutes to a few hours

See Message Template Best Practices for what gets approved on the first try.

Webhooks

Ghala points your number's webhooks at your own HTTPS endpoint, and events arrive in the standard WhatsApp Cloud API format: incoming messages, delivery updates, and read receipts, pushed in real time.

Configure it in Dashboard → Developer → Webhooks: enter your callback URL and verify token, and Ghala runs Meta's verification handshake against your endpoint immediately.

The full walkthrough (handshake code, payload examples, retry semantics) is in the Webhooks Setup Guide.

Analytics

Message stats

GET /analytics/messages

Query parameters:

  • start_date, end_date: ISO dates
  • granularity: hour, day, week, or month

Returns sent, delivered, read, and failed counts per bucket.

Errors

Error format

{
  "success": false,
  "error": {
    "code": "INVALID_PHONE",
    "message": "Phone number format is invalid",
    "details": "Expected format: country code + number"
  }
}

Common error codes

Code Status Description
UNAUTHORIZED 401 Missing or invalid API key
INVALID_PHONE 400 Bad phone format; international, no +
OUTSIDE_SESSION_WINDOW 400 Free-form message outside the 24-hour window; send a template
TEMPLATE_NOT_FOUND 404 Template doesn't exist or isn't approved
INSUFFICIENT_CREDITS 402 Top up required
RATE_LIMITED 429 Too many requests; honor X-RateLimit-Reset

Errors WhatsApp reports asynchronously (blocked recipients, expired sessions, policy rejections) arrive as failed statuses on your webhook, with Meta's error detail attached.

Retry guidance: 5xx and 429 are safe to retry with exponential backoff. 4xx errors are yours to fix; retrying the same request won't change the answer.

PreviousMessage Template Best Practices

On this page

  • Overview
  • Authentication
  • Response envelope
  • Rate limits
  • The 24-hour session window
  • Messages
  • Send a text message
  • Send a template message
  • Send a media message
  • Message statuses
  • Contacts
  • List contacts
  • Get / update / delete a contact
  • Create a contact
  • Templates
  • List / get templates
  • Create a template
  • Webhooks
  • Analytics
  • Message stats
  • Errors
  • Error format
  • Common error codes