Developers
An API you can reason about.
Every endpoint below is implemented and tested against a real database with row-level security — documented exactly as built, at pilot stage. Base URL and credentials ship with your pilot workspace; nothing here sends live messages until the Meta connection is verified for your business.
Authentication
Two credential kinds, both scoped to your seat’s permissions (a key can never exceed the person who issued it):
# Bearer token (a signed session credential)
Authorization: Bearer <token>
# API key (hashed at rest; shown once at issue)
Authorization: ApiKey ctk_...
# Multi-business: select among YOUR memberships (never grants one)
X-Convotide-Org: <org uuid>Send a message (durably)
Acceptance is idempotent: retry safely with the same key and you get the same intent, never a duplicate send. The response is a durable queue acknowledgement — no provider call has happened yet.
POST /v1/messages
{ "to": "+91...", "template": "order_shipped",
"idempotencyKey": "your-stable-key", "payload": { "order_id": "A-1" } }
202 Accepted
{ "intentId": "<uuid>", "status": "accepted",
"deduplicated": false,
"note": "durably queued; dispatch is a later worker slice" }Quota is enforced here too: no active plan returns 402 PLAN_REQUIRED; an exhausted month returns 402 QUOTA_EXCEEDED.
Webhooks (signed, persist-then-ack)
We verify the signature over the exact raw bytes before anything is stored, and the raw event is persisted before the acknowledgement — replays are absorbed forever.
POST /v1/webhooks/provider
X-Convotide-Signature: sha256=<hmac-of-raw-body>
{ "id": "<provider event id>", "type": "message.delivered",
"orgId": "<uuid>", "data": { "messageId": "<uuid>", "status": "delivered" } }
200 OK
{ "status": "acknowledged", "deduplicated": false,
"projected": { "applied": true, "current": "delivered" },
"consent": null, "botRun": null }Inbound STOP/START texts apply consent decisions in the same transaction; keyword texts fire published bots — all idempotent on the event id.
The rest of the surface
Campaigns
POST /v1/campaigns · transition (draft→scheduled→sending, guarded) · recipients (consent-gated enqueue, idempotent per contact).
Inbox
GET /v1/inbox · threads · claim with collision protection (409 if a colleague holds it) · mark read.
Analytics & export
GET /v1/analytics/summary · GET /v1/contacts/export (with per-channel consent state — your data stays yours).
Errors are uniform
{ "error": { "code": "SCOPE_DENIED",
"message": "this action requires the \"campaigns:write\" scope for your seat" } }Every refusal carries a machine-readable code and a plain-language message — the same shape from every endpoint.
Content reviewed October 2026 · owner: Zcode (implementation) · independent factual review: Codex QC, pending.