API, MCP and webhooks for developers
Connect your WhatsApp inbox to your own software, your CRM and your AI assistants. Every user creates their own API key in the dashboard; the same rules (24-hour window, approved templates) apply everywhere.
REST API
Read conversations, send messages and templates, start campaigns.
MCP (AI)
Let assistants like Claude, ChatGPT and Cursor work with your inbox.
Webhooks
Receive new messages and delivery updates instantly, signed.
The API, webhooks and MCP are available on the Pro and Business plans (including the 14-day trial).
Quick start
1. Open API & MCP in the dashboard and create a key. It is shown only once, so store it somewhere safe.
Create a key in the dashboard2. Send the key in the Authorization header with every request and make a test call:
curl https://sosyalmedya.website/api/v1/me \
-H "Authorization: Bearer smw_live_…"3. Send your first message with an approved template:
curl -X POST https://sosyalmedya.website/api/v1/messages \
-H "Authorization: Bearer smw_live_…" \
-H "Content-Type: application/json" \
-d '{
"to": "905321234567",
"template": {
"name": "randevu_hatirlatma",
"language": "tr",
"params": ["Ayşe", "yarın 10:00"]
}
}'Authentication and scopes
Send your personal API key as a Bearer token with every request. A key acts with the permissions of the user who created it and stops working immediately if it is revoked or the user is removed from the team.
Authorization: Bearer smw_live_…Scopes
| scope | |
|---|---|
| read | Reads numbers, templates, conversations, messages and campaigns. |
| send | Sends messages and templates, marks as read, starts and stops campaigns. Always includes read. |
Security
- Never use a key in a browser, a mobile app or public code; keep it on your server.
- Giving an AI assistant a read-only key is the safest way to start.
- If you suspect a key has leaked, revoke it in the dashboard and create a new one.
WhatsApp rules
- 24-hour window
- You can send free-form text within 24 hours of the customer's last message (72 hours when they came from a Click-to-WhatsApp ad). The conversation's window_open field tells you.
- Approved templates
- To start a new conversation or write when the window is closed you must send a template approved by Meta. Create templates in the dashboard and list them with /templates.
- Opt-in
- Only message people who agreed to receive WhatsApp messages from you. Campaigns require opt_in_confirmed: true.
- Phone format
- Country code and digits only: 905321234567. Turkish mobile numbers written as 0532… or 532… are fixed automatically.
- Fees
- Meta's per-message fees are charged directly to your WhatsApp Business account; the API adds no message markup.
Endpoints
The base URL is below. All bodies and responses are JSON; times are ISO 8601 (UTC).
https://sosyalmedya.website/api/v1| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /me | read | Business, user, key scopes and connected numbers |
| GET | /numbers | read | Connected WhatsApp numbers |
| GET | /templates | read | Templates (?number_id=&status=APPROVED) |
| GET | /conversations | read | Conversations by last message (?search=&number_id=&page=) |
| GET | /conversations/{id} | read | Conversation and latest messages (?limit=1–150) |
| POST | /conversations/{id}/read | send | Mark as read and send a read receipt |
| POST | /messages | send | Send text or a template |
| GET | /messages/{id} | read | Message and delivery status |
| GET | /campaigns | read | Campaigns |
| POST | /campaigns | send | Opt-in bulk template sending |
| GET | /campaigns/{id} | read | Campaign and counters |
| POST | /campaigns/{id}/cancel | send | Stop a campaign |
Examples
Start a conversation with a template
curl -X POST https://sosyalmedya.website/api/v1/messages \
-H "Authorization: Bearer smw_live_…" \
-H "Content-Type: application/json" \
-d '{
"to": "905321234567",
"template": {
"name": "randevu_hatirlatma",
"language": "tr",
"params": ["Ayşe", "yarın 10:00"]
}
}'If you have more than one number, also pass number_id when sending with to.
Reply in an open conversation (within 24 hours)
curl -X POST https://sosyalmedya.website/api/v1/messages \
-H "Authorization: Bearer smw_live_…" \
-H "Content-Type: application/json" \
-d '{ "conversation_id": "CONVERSATION_ID", "text": "Merhaba, nasıl yardımcı olabilirim?" }'Find unread conversations
curl "https://sosyalmedya.website/api/v1/conversations?page=1" \
-H "Authorization: Bearer smw_live_…"JavaScript (Node 18+)
const res = await fetch("https://sosyalmedya.website/api/v1/messages", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SMW_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
to: "905321234567",
template: { name: "randevu_hatirlatma", language: "tr", params: ["Ayşe", "yarın 10:00"] },
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error.code}: ${data.error.message}`);
console.log(data.message.id, data.message.status);Python
import os, requests
res = requests.post(
"https://sosyalmedya.website/api/v1/messages",
headers={"Authorization": f"Bearer {os.environ['SMW_API_KEY']}"},
json={
"to": "905321234567",
"template": {"name": "randevu_hatirlatma", "language": "tr", "params": ["Ayşe", "yarın 10:00"]},
},
timeout=30,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{data['error']['code']}: {data['error']['message']}")
print(data["message"]["id"], data["message"]["status"])Errors
Errors return an HTTP status code and the body below. Branch on code; message is meant for humans.
{
"error": {
"code": "window_closed",
"message": "The 24-hour customer service window is closed …",
"detail": "no_conversation"
}
}| code | HTTP | Meaning |
|---|---|---|
| unauthorized | 401 | Key missing, wrong or revoked |
| insufficient_scope | 403 | Read-only key; the send scope is required |
| plan_upgrade_required | 402 | Plan does not include the API (Pro or Business required) |
| subscription_inactive | 402 | Subscription is not active |
| validation_failed | 422 | Invalid field; detail tells which one |
| not_found | 404 | Not found or not owned by this business |
| window_closed | 409 | 24-hour window closed or no conversation yet: send a template |
| no_wa_account | 409 | No WhatsApp number connected |
| template_not_available | 422 | Template missing, not approved or not in that language |
| undeliverable | 422 | The number may not be on WhatsApp |
| rate_limited | 429 | Rate limit exceeded; wait for Retry-After |
Rate limits
- 120 requests per minute per key (or connected app).
- Of those, at most 30 sends per minute (messages, read receipts, campaigns). AI (MCP) connections are limited to 10 sends per minute.
- Over the limit you get 429 with Retry-After: 60. For bulk sending use campaigns instead of one-by-one messages; campaigns are sent in batches that respect Meta's throughput limits.
Webhooks
The account owner can add up to 3 https URLs under API & MCP → Webhooks in the dashboard. Each event arrives at your URL as a POST with a JSON body.
Events
| event | |
|---|---|
| message.received | A new message from a customer (text, media, location, reaction…). |
| message.status | The status of a message you sent changed: sent, delivered, read, failed. |
Headers
| header | |
|---|---|
| X-SMW-Event | Event type |
| X-SMW-Event-Id | Event id; use it to drop duplicates |
| X-SMW-Delivery | Id of this delivery |
| X-SMW-Signature | t=<unix time>,v1=<signature> |
Example body
{
"id": "evt_5f0c2a1e-…",
"type": "message.received",
"created_at": "2026-10-19T09:41:12.000Z",
"data": {
"message": {
"id": "…", "conversation_id": "…", "number_id": "…",
"direction": "in", "type": "text", "text": "Fiyat alabilir miyim?",
"status": "received", "source": "contact", "media": null,
"created_at": "2026-10-19T09:41:11.000Z"
},
"conversation": {
"id": "…", "contact": { "wa_id": "905321234567", "name": "Ayşe" },
"unread_count": 1, "window_open": true,
"window_expires_at": "2026-10-20T09:41:11.000Z"
},
"number": { "id": "…", "display_phone_number": "+90 532 000 00 00" }
}
}Verifying the signature
The signature is HMAC-SHA256("<t>.<raw body>") with the secret shown in the dashboard. Verify the raw body before parsing it and reject timestamps older than 5 minutes.
Node.js
import crypto from "node:crypto";
// rawBody: ayrıştırılmamış gövde (string) · header: X-SMW-Signature
export function verifySignature(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
return expected.length === parts.v1?.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}Python
import hashlib, hmac, time
def verify_signature(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
if abs(time.time() - int(parts["t"])) > 300:
return False
expected = hmac.new(secret.encode(), parts["t"].encode() + b"." + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))Delivery and retries
- An event counts as delivered when you return 2xx within 10 seconds.
- Otherwise it is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 12 h and 24 h (8 attempts in total).
- After 25 consecutive failures the URL is paused automatically; you can re-enable it in the dashboard.
- The same event may arrive more than once; deduplicate with X-SMW-Event-Id.
MCP: AI assistants
MCP (Model Context Protocol) is an open standard that lets AI assistants use external systems as tools. Our MCP server lets your assistant read your inbox and, with your approval, send messages.
MCP server
https://sosyalmedya.website/api/mcpClaude and ChatGPT (one-click connect)
- Claude: paste the MCP URL in Settings → Connectors → "Add custom connector". ChatGPT: add a new app (connector) in developer mode with the same URL.
- Sign in with your sosyalmedya.website account on the page that opens, choose the permissions and approve. If you do not grant sending, the connection is read-only.
- You can disconnect anytime under API & MCP → Connected AI apps in the dashboard.
With an API key (Claude Code, Cursor, VS Code, Windsurf)
These tools support OAuth too; if you prefer not to share a key, add just the URL and a sign-in page opens on first use.
Claude Code
# OAuth (tarayıcıda giriş) / OAuth (sign in in the browser)
claude mcp add --transport http sosyalmedya https://sosyalmedya.website/api/mcp
# API anahtarıyla / With an API key
claude mcp add --transport http sosyalmedya https://sosyalmedya.website/api/mcp \
--header "Authorization: Bearer smw_live_…"Cursor — .cursor/mcp.json
{
"mcpServers": {
"sosyalmedya": {
"url": "https://sosyalmedya.website/api/mcp",
"headers": {
"Authorization": "Bearer smw_live_…"
}
}
}
}VS Code — .vscode/mcp.json
{
"servers": {
"sosyalmedya": {
"type": "http",
"url": "https://sosyalmedya.website/api/mcp",
"headers": {
"Authorization": "Bearer smw_live_…"
}
}
}
}Windsurf — ~/.codeium/windsurf/mcp_config.json
{
"mcpServers": {
"sosyalmedya": {
"serverUrl": "https://sosyalmedya.website/api/mcp",
"headers": {
"Authorization": "Bearer smw_live_…"
}
}
}
}Tools
| Tool | Scope | What it does |
|---|---|---|
| get_account | read | Business, plan, connection scopes and numbers |
| list_whatsapp_numbers | read | Connected numbers |
| list_templates | read | Approved templates and their variable counts |
| list_conversations | read | Conversations, unread counts, window state |
| get_conversation | read | Latest messages of a conversation |
| get_message_status | read | Delivered / read / failed |
| send_text_message | send | Free-form text in an open window |
| send_template_message | send | Approved template (new conversation or closed window) |
| mark_conversation_read | send | Mark as read |
Safety principles
- Sending tools are marked as having external effects, so assistants ask for your confirmation before sending.
- Bulk campaigns are intentionally not available over MCP: an AI messaging many people by mistake could lower your number's quality rating. Start campaigns from the dashboard or the REST API.
- Customer messages are marked as untrusted content and the assistant is told not to follow instructions inside them. Grant sending only to assistants you trust.
- The assistant only sees what the connected user can see.
OpenAPI and versioning
A machine-readable description of every endpoint is available as OpenAPI 3.1. Import it into Postman, n8n, Make or ChatGPT Actions.
https://sosyalmedya.website/api/v1/openapi.jsonVersion 1 is stable: new fields and endpoints may be added, existing ones do not change. A breaking change would ship as /api/v2 and be announced in advance.
Questions: [email protected]