REST API, webhooks, OAuth flows for every channel, sandbox tokens. UK-hosted, audit-logged, idempotent. Designed so your team owns the integration, not us.
Every request needs a bearer token. Tokens are scoped per-channel (so the WhatsApp integration can't read LinkedIn messages) and rotatable from the dashboard. We will never ship long-lived tokens that grant read+write on everything — least-privilege is the default.
GET /v1/messages?since=2026-05-21T00:00:00Z Authorization: Bearer cls_live_3f7a… Accept: application/json
cls_live_ hit the live customer environment. Sandbox tokens are cls_test_ and never touch real channel APIs.Single base URL, semver-locked API version in the path. We do not break /v1. Breaking changes ship under /v2 with at least 12 months of overlap.
# Production https://api.closer.agentnetworks.co.uk/v1 # Sandbox https://api.closer.agentnetworks.co.uk/v1?env=sandbox
List, fetch, and send messages across every connected channel. The shape is the same whether the message came from WhatsApp, email, or LinkedIn — the channel field tells you which.
List inbound messages, paginated by cursor. Filter with channel, status (pending | replied | escalated), or since.
{
"data": [
{
"id": "msg_8x1k…",
"channel": "whatsapp",
"from": "+447700900123",
"body": "Hi, are you open Saturday?",
"received_at": "2026-05-22T07:14:03Z",
"status": "replied",
"reply": {
"id": "rep_a3b9…",
"body": "Yes — 9am to 4pm Saturday. See you then!",
"sent_at": "2026-05-22T07:14:51Z",
"mode": "autonomous"
}
}
],
"next_cursor": "cur_2pq8…"
}
Send a reply manually (overrides any pending CLOSER draft). Idempotency-Key header strongly recommended.
POST /v1/messages/msg_8x1k.../reply Idempotency-Key: 2026-05-22-paul-001 Content-Type: application/json { "body": "Yes, we're open 9-4 Saturday.", "send_as": "human" }
Read or update the tone profile that CLOSER uses to write replies. Useful if you want to push a tone change from your CRM (e.g. switch to "post-incident apology" mode after an outage).
{
"formality": 3, // 1 casual → 5 formal
"warmth": 4,
"sentence_length": "short",
"emoji": "sparingly",
"sign_off": "— Paul",
"escalation_topics": ["refunds", "complaints", "custom-quotes"]
}
Subscribe to events. We deliver with exponential backoff (1s, 4s, 16s, 64s, 256s, then dead-letter to your dashboard). Signed with HMAC-SHA256 in X-Closer-Signature.
message.received — new inbound on any channelmessage.replied — CLOSER (or human) sent a replymessage.escalated — bumped to human queuetone_profile.updated — profile changedintegration.disconnected — channel OAuth expired or revokedPOST https://your-app.com/webhooks/closer X-Closer-Signature: t=1716368483,v1=4f8e… X-Closer-Event: message.escalated Content-Type: application/json { "event": "message.escalated", "message_id": "msg_8x1k…", "reason": "topic:refund", "draft": "I'm sorry to hear that — let me check your order..." }
Every channel uses its own OAuth dance. You don't paste passwords anywhere — you click "Connect" and the channel provider does the rest. Tokens are stored encrypted at rest (AWS KMS, customer-managed keys available on the Autonomous tier).
Identical surface, no real channels. Hit it with any cls_test_ token. Inbound messages can be faked via POST /v1/sandbox/inject. Replies don't go anywhere — they just come back through the webhook so you can wire your downstream systems end-to-end before going live.
POST /v1/sandbox/inject Authorization: Bearer cls_test_… { "channel": "whatsapp", "from": "+447700900999", "body": "What time do you open?" }
Retry-After.{ "error": { "code": "...", "message": "...", "trace_id": "..." } }. Always pass us the trace_id if you need support.Need a signed contract before we issue prod tokens? DPA template, sub-processor list, and SOC2 readiness pack on the Security page.
Sandbox is free, forever. Wire up the webhooks, fake a few inbound messages, decide if the shape fits your stack — then talk pricing.