Skip to content
SocialMate

SocialMate Local API

Responses & errors

The v1.1 { data } / { error } envelope, pagination, status codes, and the error catalogue.

The API speaks JSON in and JSON out. As of v1.1 every response is wrapped in a small, predictable envelope so a client always parses the same two shapes.

Success — the data envelope

Every 2xx response carries its payload under data:

{ "data": { "sent": true, "messageId": "3EB0…" } }

The shape of the payload itself doesn't change — only its position under data. A 202 auto-queue response, for instance, is { "data": { "queued": true, "itemId": "…" } }.

Errors — the error envelope

Every 4xx/5xx response carries an error object with a stable code, a human message, and any extra diagnostic fields the route provides (reason, retryAfterMs, required, …):

{
  "error": {
    "code": "rate_limited",
    "message": "Per-key request rate exceeded",
    "retryAfterMs": 1200
  }
}

Pagination

List endpoints that page (contacts, messages, media) return their rows under data alongside a pagination block. Use limit (max 500) and offset query params; the total tells you when to stop.

The queue endpoints are the exception: they return { "data": { "items": [ … ], "total": n, "hasMore": bool } } — no separate pagination block. See Queue.

{
  "data": [ /* rows */ ],
  "pagination": { "limit": 100, "offset": 0, "total": 1283 }
}

Legacy (pre-1.1) shape

If you built against the older bare shapes, pin them per request with a header — the payload is then returned unwrapped. The legacy envelope is supported for back-compat and will be removed in a future v2.

curl http://localhost:3456/v1/accounts \
  -H "x-api-key: YOUR_KEY" \
  -H "X-SocialMate-Envelope: v1"

Binary responses (the media file and thumbnail streams) and the /docs spec routes are never wrapped.

HTTP status codes

CodeMeaning
200OK.
202Accepted — the send was auto-queued (Pro) because anti-ban blocked an immediate send.
206Partial content — a ranged media file stream.
400Validation failed (bad/missing field, malformed body).
401Missing or invalid x-api-key.
402The feature requires Pro — the body names the missing feature.
403The key lacks the required scope — error.code insufficient_scope, with required and have naming the gap.
404Account, contact, group, media, key, or webhook not found.
409Conflict — e.g. the account isn't connected, or media isn't downloaded yet.
422Unprocessable — well-formed but semantically rejected (error.code unprocessable).
429Rate-limited (per-key bucket) or anti-ban blocked the send. Honour Retry-After.
500Server error.
502The send/operation reached WhatsApp but failed downstream.
503Service temporarily unavailable (error.code unavailable).

The error.code catalogue

Most responses carry a status-derived error.code, and routes attach extra descriptive fields (reason, required, have, feature, tier, retryAfterMs) for the detail. The status-derived set:

codeStatusWhen
bad_request400Validation failed (bad/missing field, malformed body).
unauthorized401Missing or invalid x-api-key.
license_required402Pro feature — see feature / tier.
insufficient_scope403Scope too narrow — see required / have.
not_found404No such account, contact, group, media, key, or webhook.
conflict409Account not connected, or media not downloaded yet.
unprocessable422Well-formed but semantically rejected.
rate_limited429Per-key bucket exceeded, or anti-ban blocked the send — see retryAfterMs and (anti-ban only) reason.
upstream_error502Reached WhatsApp but failed downstream.
unavailable503Service temporarily unavailable.
server_error500Unexpected server error.

Some routes return a more specific machine code instead of the status-derived one, so don't assume the status alone tells you the code. These survive the envelope: insufficient_scope, account_scope_mismatch, account_suspended_free, invalid_url, invalid_contact_id, bulk_import_disabled, context_exists, and the anti-ban family ANTI_BAN_<REASON> (e.g. ANTI_BAN_SIGNAL_RATE_LIMIT). When you need to branch on an anti-ban block, use error.reason — it is stable across every route.

Anti-ban blocks return 429 with an error.reason naming the specific block — rate_limit, warming, night_mode, risk_critical, paused_global, paused_account, banned, unknown_account, duplicate_text, or signal_rate_limit (see Anti-ban status). Scope errors return 403 insufficient_scope with required / have (see Authentication).

Free forever · no card Download free