REST API

Every endpoint is org-scoped. Pass Authorization: Bearer <token> and x-org-id: <orgId> on each request.

Send a WhatsApp message

POST /orgs/:orgId/conversations/:conversationId/messages

curl -X POST https://webhook.badala.app/orgs/<orgId>/conversations/<convId>/messages \
  -H 'Authorization: Bearer <token>' \
  -H 'x-org-id: <orgId>' \
  -H 'Content-Type: application/json' \
  -d '{
    "type": "TEXT",
    "body": "Hello from the API"
  }'

For media, send the public URL (any host CDN works) plus type:

{
  "type": "IMAGE",
  "mediaUrl": "https://assets.badala.app/orgs/<orgId>/uploads/abc.jpg",
  "caption": "Optional caption"
}

Send a template (broadcast / first-touch)

Required if the customer hasn't messaged in the last 24 hours. Templates must be approved in Meta Business Manager beforehand.

POST /orgs/:orgId/channels/:channelId/templates/send
{
  "to": "+96567666156",
  "templateName": "appointment_reminder",
  "templateLanguage": "en",
  "variables": ["Yousef", "May 9 at 4pm"]
}

Contacts

MethodPathPurpose
GET/orgs/:orgId/contacts?search=&take=&skip=List + search
GET/orgs/:orgId/contacts/:contactIdDetail incl. conversations
POST/orgs/:orgId/contactsUpsert (by phone)
PATCH/orgs/:orgId/contacts/:contactIdUpdate
POST/orgs/:orgId/contacts/importBulk CSV import

Conversations

MethodPathPurpose
GET/orgs/:orgId/conversationsList (filtered by status, channel, assignee)
GET/orgs/:orgId/conversations/:id/messagesMessage history
PATCH/orgs/:orgId/conversations/:id/statusOpen / closed
PATCH/orgs/:orgId/conversations/:id/assigneeAssign teammate

Real-time updates

The web app uses Socket.IO at the same origin (wss://webhook.badala.app) for live message push. For server-to-server integrations, use outbound webhooks instead — they're durable and signed.

Rate limits

Outbound WhatsApp messages: gated by Meta's tier (Tier 1 = 80 msg/sec, Tier 2 = 200 msg/sec, etc). Broadcasts respect this internally with a configurable gap. Direct API calls are not rate-limited by Badala beyond what Meta enforces upstream.