Send and receive messages on all your connected channels — WhatsApp, Telegram, Messenger, Instagram, TikTok — from your own systems, receive events by webhook, and even plug in your own AI key.
Create API keys from your dashboard: Messaging API page (each key is an independent instance with its own allowed channels and webhook). Keys are shown once — store them securely.
Base URL: https://thikaa.com/<your-store>/api/messaging/ Authorization: Bearer <api_key> (or X-API-Key: <api_key>)
POST /api/messaging/?action=send
| Field | Required | Description |
|---|---|---|
channel | ✓ | One of: wd (WhatsApp QR) · wa (WhatsApp API) · tg · fb · ig · tt |
to | ✓ | Recipient — phone number with country code for WhatsApp; for WhatsApp QR (wd), you may also use a Baileys group JID such as 120363012345678901@g.us; platform user id otherwise |
message | ✓ | Text to send (UTF-8, Arabic fully supported) |
media_url | — | Optional public HTTPS URL of an image / document to attach |
media_type | — | Hint like image, document |
curl -X POST "https://thikaa.com/<your-store>/api/messaging/?action=send" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channel": "wd",
"to": "9647701234567",
"message": "تحديث الوظائف الأسبوعي 👇 ..."
}'
{ "ok": true, "message_id": "...", "channel": "wd", "to": "9647701234567" }
A pre-approved template is the only lawful way to message a WhatsApp Cloud API (wa) contact who has never written to you, or to reopen a chat after the 24-hour window has closed. Without one, action=send returns 409 no_existing_conversation.
GET /api/messaging/?action=templates
curl "https://thikaa.com/<your-store>/api/messaging/?action=templates" \
-H "Authorization: Bearer YOUR_API_KEY"
{ "ok": true, "count": 1, "templates": [
{ "name": "order_update", "language": "en", "status": "APPROVED",
"category": "UTILITY", "body": "Hi {{1}}, your order {{2}} is ready.", "variables": 2 }
] }
POST /api/messaging/?action=send — channel wa only
| Field | Required | Description |
|---|---|---|
template.name | ✓ | Approved template name |
template.language | — | Language code, e.g. en, ar, en_US. Defaults to en |
template.variables | — | Array of strings filled into the body placeholders {{1}}, {{2}} … in order |
template.components | — | Full Meta components array instead of variables — for header media, buttons or named parameters |
from | — | Which of your WhatsApp numbers to send from — phone-number-id, display number or label. Defaults to your first number |
curl -X POST "https://thikaa.com/<your-store>/api/messaging/?action=send" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channel": "wa",
"to": "+994105156977",
"template": {
"name": "order_update",
"language": "en",
"variables": ["Nurlan", "#1042"]
}
}'
{ "ok": true, "message_id": "wamid...", "conversation_id": 87,
"template": "order_update", "language": "en", "delivery": "sent" }
message.received with that same conversation_id, and normal text sending works from then on — no second thread, no 409.GET /api/messaging/?action=status — returns the key’s instance status, allowed channels and current balance.
curl "https://thikaa.com/<your-store>/api/messaging/?action=status" \ -H "Authorization: Bearer YOUR_API_KEY"
Set a webhook URL per API key (Messaging API page). Every inbound customer message on the key’s channels is POSTed to your endpoint as JSON, signed with your key’s secret:
POST <your-webhook-url>
Content-Type: application/json
X-Thikaa-Event: message.received
X-Thikaa-Signature: sha256=<hmac_sha256(body, webhook_secret)>
{ "event": "message.received", "instance_id": "...", "data": {
"channel": "wa",
"conversation_id": 1290,
"message_id": 84990,
"from": "+9665XXXXXXXX",
"account": { "id": "1297236690138576", "number": "+994 10 515 69 77", "label": "nurlan_dev" },
"text": "Where is my order?",
"attachments": []
} }
@g.us) as the conversation identity, so every participant lands in one shared Thikaa conversation. Each group event includes a whatsapp_group object with is_group, group_jid, participant, and participant_name. Replies sent with channel: wd and the same group JID go back to the group. Broadcasts and newsletters are excluded. WhatsApp Cloud API (wa) does not support normal group chats.$body = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_THIKAA_SIGNATURE'] ?? '';
$ok = hash_equals('sha256=' . hash_hmac('sha256', $body, $secret), $sig);
const crypto = require('crypto');
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers['x-thikaa-signature'] || ''));
Tick the events you want on each API key. The X-Thikaa-Event header and the event field always say which one you are handling.
| Event | Fires when |
|---|---|
message.received | a customer sends you a message |
message.sent | anything leaves your workspace — origin is api, bot, dashboard, or phone for a reply typed straight into WhatsApp on the linked handset |
message.status | a receipt moves a message forward: sent → delivered → read. WhatsApp reports all three; Messenger reports delivered and read; Instagram reports read. TikTok and Telegram expose no receipts, so nothing is emitted for them. |
message.reaction | someone adds or removes a reaction |
comment.received | someone comments on a Facebook post, Instagram post or TikTok video |
channel.connected | a channel finished connecting — an OAuth link completed, or a WhatsApp number finished pairing |
channel.disconnected | a channel was removed, by API or in the dashboard |
Built for platforms that resell us: your customer never sees a Thikaa login. You mint a connect link, they authorize on Facebook or TikTok, and the page lands in your workspace.
curl "https://thikaa.com/<your-store>/api/messaging/?action=channels" \
-H "Authorization: Bearer YOUR_API_KEY"
{ "ok": true, "channels": [
{ "channel": "wd", "id": "1000017", "name": "Lavida Travel", "identifier": "96555060830", "status": "connected" },
{ "channel": "fb", "id": "100877191952677", "name": "Pawlo Butchery", "status": "connected" },
{ "channel": "ig", "id": "17841478342647968", "name": "Pawlo Butchery", "status": "connected" },
{ "channel": "tt", "id": "-000HFd...", "name": "pawlo_butcher", "status": "connected" } ],
"counts": { "wd": 1, "fb": 1, "ig": 1, "tt": 1 } }
GET /api/messaging/?action=connect-link&channel=fb # fb | ig | tt
&redirect_url=https://your-app.com/done?client=42
{ "ok": true, "channel": "fb",
"connect_url": "https://www.facebook.com/v21.0/dialog/oauth?...",
"redirect_url": "https://your-app.com/done?client=42" }
Send your client to connect_url. When they finish, we return them to your redirect_url with the outcome appended — ?thikaa_status=success&channel=fb&connected=2&ids=... — and POST you a signed channel.connected event per connected surface.
?thikaa_status=success. Use it to show a result page; write to your database on the signed event.One Meta authorization covers both surfaces: connecting a Facebook page also connects its Instagram DMs, and you get two channel.connected events for it.
POST /api/messaging/?action=connect-telegram
{ "bot_token": "8765196861:AAG..." } # from @BotFather
{ "ok": true, "channel": "tg", "status": "connected", "bot_username": "YourBot" }
Pairing code is the primary flow: your client is usually holding the very phone being linked, where a QR on that same screen cannot be scanned.
POST /api/messaging/?action=create-session { "label": "Clinic Riyadh" }
-> { "session_token": "wds_...", "account_id": 1000021 }
POST /api/messaging/?action=pair-code { "session_token": "wds_...", "phone": "+9665XXXXXXXX" }
-> { "pairing_code": "ABCD-EFGH" } # client types this into WhatsApp
GET /api/messaging/?action=session-status&session_token=wds_...
GET /api/messaging/?action=session-qr&session_token=wds_... # desktop fallback
POST /api/messaging/?action=delete-session
channel.connected instead of polling session-status in a loop.POST /api/messaging/?action=disconnect-channel
{ "channel": "fb", "id": "100877191952677" } # fb | ig | tg | tt
{ "ok": true, "channel": "fb", "status": "disconnected",
"also_disconnected": [ { "channel": "ig", "id": "17841478342647968" } ] }
For Facebook this is a full teardown: we unsubscribe our app from the page at Meta so delivery actually stops, then remove its routing and bot links. channel=ig detaches only the Instagram account and leaves Messenger working. WhatsApp QR uses delete-session.
Comments are a separate surface from DMs, with their own ids and their own reply mechanism. One event to receive them, two endpoints to read and answer.
{ "event": "comment.received", "data": {
"platform": "fb", # fb | ig | tt
"comment_id": "1221...._1795...",
"post_id": "113700101731161_1220987...",
"parent_id": null,
"text": "How much is this?",
"from": { "id": "7051...", "name": "Adham Ammar" },
"conversation_id": 1841
} }
POST /api/messaging/?action=comment-reply
{ "comment_id": "1221...._1795...",
"message": "It is 200 SAR",
"private_message": "Here is the full list..." } # Meta private reply, opens a real DM
GET /api/messaging/?action=comments&platform=tt&limit=50
fb and ig events fire the instant a comment is posted. TikTok offers no comment webhook for our app type, so TikTok comments are polled and arrive within a few minutes. Replies are immediate on all three.private_message there returns a clear error rather than silently doing nothing, and its comment replies are text only. Instagram public replies require Meta’s instagram_manage_comments permission on our app.Separately from the Messaging API, the platform can POST admin-level events to a single URL (Settings → Webhooks). Useful events include:
| Event | Fires when |
|---|---|
message-sent | a message is sent in any conversation |
bot-message | the AI bot replies |
new-messages | new inbound customer messages |
new-conversation / new-conversation-created | a new conversation starts |
conversation-status-updated | a conversation is resolved / archived |
sms-sent · email-sent | an SMS / email notification goes out |
You can run your bots on your own AI account instead of platform credit: connect an OpenAI, Google Gemini or Anthropic Claude API key from the AI Providers page in your dashboard, then select that connection in your bot’s model settings. Your key is stored per-tenant and used only for your own bots.
| HTTP | Error | Meaning |
|---|---|---|
| 401 | missing_api_key / invalid_api_key | Key absent, disabled or revoked |
| 402 | insufficient_balance | Account out of credit / inactive |
| 403 | channel_not_allowed | This key is not permitted to use that channel |
| 400 | bad_redirect_url | redirect_url is not an absolute HTTPS URL on a resolvable public hostname |
| 404 | unknown_comment · not_connected | That comment was never delivered to you, or nothing with that id is connected here |
| 405 | method_not_allowed | Wrong HTTP method |
| 429 | rate_limited | Per-key rate limit exceeded (default 60 requests/minute) |
| 502 | graph_error · tiktok_error | Meta or TikTok refused the call — their own message is passed through verbatim |
Questions? Message us on WhatsApp from the site — a human (or our own bot 😄) will help.