Developer Documentation

Send WhatsApp messages with an API you already know

If you've called the WhatsApp Cloud API, you already know ours. Same path, same JSON body, same Bearer auth — point your code at a new base URL and you're done.

01Quickstart

Three things and your first message is on its way: the base URL, a device key, and the messages endpoint.

Base URL
https://api.smartsybox.com
Authentication
Authorization: Bearer <YOUR_DEVICE_KEY>
Content type
application/json

The Endpoint

POST /v26.0/{phone-number-id}/messages
The version segment is accepted in any v form (v26.0, v1.0, …) — keep whatever your current code uses. The {phone-number-id} is your WhatsApp number's ID, exactly as with Meta.

02Send your first message

A plain text message. The body is the standard Cloud API payload — we pass it through to Meta untouched.

cURL
curl -X POST https://api.smartsybox.com/v26.0/PHONE_NUMBER_ID/messages \
  -H "Authorization: Bearer YOUR_DEVICE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messaging_product": "whatsapp",
    "to": "15551234567",
    "type": "text",
    "text": { "body": "Hello from SyBox 👋" }
  }'
C# · HttpClient
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;

var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", "YOUR_DEVICE_KEY");

string json = @"{
  ""messaging_product"": ""whatsapp"",
  ""to"": ""15551234567"",
  ""type"": ""text"",
  ""text"": { ""body"": ""Hello from SyBox"" }
}";

var res = await http.PostAsync(
    "https://api.smartsybox.com/v26.0/PHONE_NUMBER_ID/messages",
    new StringContent(json, Encoding.UTF8, "application/json"));

Console.WriteLine(await res.Content.ReadAsStringAsync());

The Response

You get back exactly what Meta returns, with the same HTTP status — so any existing Cloud API client library parses it unchanged.

200 OK · application/json
{
  "messaging_product": "whatsapp",
  "contacts": [{ "input": "15551234567", "wa_id": "15551234567" }],
  "messages": [{ "id": "wamid.HBgLMTU1NTEyMzQ1NjcVAgARGBI…" }]
}

03Any message type works

Because the body is forwarded untouched, every Cloud API message type is supported — templates, images, documents, interactive messages. Here's a template send.

Request body · template
{
  "messaging_product": "whatsapp",
  "to": "15551234567",
  "type": "template",
  "template": {
    "name": "hello_world",
    "language": { "code": "en_US" }
  }
}

04Show your data beside the chat

When an agent opens a conversation, SyBox calls YOUR endpoint and shows what you return right beside the chat — the customer's orders, balance, tickets. We send the customer's phone, or their email on an email conversation.

Request · SyBox → your endpoint
POST YOUR_ERP_URL
Authorization: Bearer YOUR_ERP_KEY
X-Sybox-Action: lookup

# body: { "data": "<json string>" } — the decoded "data":
{
  "phone": "15551234567",
  "email": "customer@example.com",
  "ref": ""
}
Your reply · application/json
{
  "renderAs": "table",
  "content": [
    { "Order": "#10432", "Status": "Shipped", "Total": "$1,250.00" }
  ]
}

renderAs

Set renderAs to tell SyBox how to display your content — one of:

table
Rows & columns. content = an array of flat objects (or { rows: [ … ] }). Object keys become the column headers.
cards · kpi
Compact tiles (a balance, an order count). content = an array of { label, value } (or { cards: [ … ] }).
feed · list · thread
A timeline of rich cards. content = an array of { title, text, date, tags, media:[{ type, url, name }] }.
message
A single message-style card (one record laid out as a note).
html
Your own trusted HTML — content = a string. Use only for markup you generate yourself.
sections
Several blocks at once: content = { sections: [ { title, renderAs, content } ] } — mix a table + cards + a feed in one reply.
json
The default when renderAs is omitted — SyBox pretty-prints your content as-is.

Add a sybox_ref to any table row or card and it becomes clickable — SyBox calls you again with that ref so you can return the single record's detail.

We send both phone and email — either may be empty. Match on whichever identifies the customer in your system, and reply with { renderAs, content }.

Product search (X-Sybox-Action: products)

The same endpoint can also answer product searches. The agent types what the customer is asking for, SyBox sends it as "q", and your reply is drawn as product cards next to the chat — with a one-tap Send that puts the product link in the composer.

Request · SyBox → your endpoint
POST YOUR_ERP_URL
Authorization: Bearer YOUR_ERP_KEY
X-Sybox-Action: products

# body: { "data": "<json string>" } — the decoded "data":
{
  "q": "wireless keyboard",
  "phone": "15551234567",
  "email": "customer@example.com",
  "ref": ""
}
Your reply · application/json
{
  "content": [
    {
      "name":      "Wireless Keyboard K380",
      "price":     449,
      "oldPrice":  520,
      "currency":  "USD",
      "available": true,
      "stock":     12,
      "image":     "https://cdn.example.com/k380.jpg",
      "link":      "https://shop.example.com/p/k380",
      "note":      "Ships in 24h"
    }
  ]
}
name
The only required field. A product with nothing but a name still renders as a tidy card.
price · oldPrice · currency
Numbers, not formatted strings. Send oldPrice only when there is a genuine previous price — the discount badge is calculated from the pair.
available · stock
available is true/false; stock is an optional count shown beside it. Omit them entirely if you do not track stock — an absent field draws nothing, and is never read as out of stock.
image
A direct https image URL. Anything that fails to load falls back to a neutral placeholder.
link
The product page URL. Without it the card is read-only — Send to chat appears only when there is something to send.
note
One short free line under the price — delivery time, a variant, a condition.
Only the endpoint you already built: same URL, same key, same { "data": "…" } envelope. Look at X-Sybox-Action to tell a product search from a customer lookup. Reply with content as a plain array (or { items: [ … ] }).

05File a case from your own system

This is the route an ERP or accounting system uses. Your program does not send a formatted message about a complaint — it asks SyBox to open the case. One record, one place, one number your staff can quote back to the customer.

cURL
curl -X POST "https://api.smartsybox.com/v1/case" \
  -H "Authorization: Bearer $SYBOX_CASE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_unique_id": "CUST-10432",
    "header": "Screen replacement not delivered",
    "description": "Paid on 12 Aug, promised in 3 days, still nothing.",
    "ext_ref": "TICKET-99817"
  }'

The fields

customer_unique_id
Your own record id for the customer — the same key customer sync uses. Falls back to phone when you have no id.
header / description
At least one is required. The header is the line staff see in the list.
ext_ref
Your ticket number. Send it. A retry with the same ext_ref returns the case that already exists instead of filing a twin — making the call safe to repeat after a timeout.
The customer must already exist. A case filed against a customer nobody can find is refused rather than inventing one. Create the customer with POST /v1/sync first.
Use a key of kind 'case'. A relay key (the one that sends messages) is refused here with 403, on purpose: a key that can message your customers should not also be able to open records in your CRM.

06Push a message in from another program

For anything that is not WhatsApp: a note from your point of sale, an alert from a device, a line from a legacy system. It lands in the right conversation, keyed by phone number, marked with the name of the program that sent it.

cURL
curl -X POST "https://api.smartsybox.com/v1/inbound" \
  -H "Authorization: Bearer $SYBOX_INBOUND_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "15551234567",
    "text": "Order 4821 has left the warehouse."
  }'
Inbound only, by design. This route writes into the conversation; it does not send anything to the customer. Use the messages route above for that.

07Errors

Errors are returned verbatim from Meta, with Meta's own status code and error shape — nothing is rewritten.

4xx · application/json
{
  "error": {
    "message": "(#131030) Recipient phone number not in allowed list",
    "type": "OAuthException",
    "code": 131030
  }
}

08Getting a device key

Keys are created inside your workspace and carry the routing themselves — no subdomain, no account id in the request.

  • Open your workspace → Settings → API keys and create a device key.
  • Each key is bound to one number and can be revoked at any time.
  • Your real WhatsApp token never leaves our server — the key stands in for it.
  • The gateway is off by default; enabling API access for the workspace switches it on.

WhatsApp, Messenger, and Instagram are trademarks of Meta Platforms, Inc.
SyBox™ is an independent product built on Meta Cloud API.

SMART SOUQ

SMART SOUQ SARL AU · RC Marrakech N° 154779 · RC Fès N° 87323 · ICE 003587569000031
© 2026. All rights reserved.