For developers & IT

Wire CLOSER into the rest of your stack.

REST API, webhooks, OAuth flows for every channel, sandbox tokens. UK-hosted, audit-logged, idempotent. Designed so your team owns the integration, not us.

API uptime · 30 days
99.98%
Healthy
Avg response · p95
142 ms
Within SLO
Region
UK · eu‑west‑2
AWS London
Version
v1 · stable
SemVer locked

Authentication

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
Tip: Tokens prefixed cls_live_ hit the live customer environment. Sandbox tokens are cls_test_ and never touch real channel APIs.

Base URL & versioning

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

Messages API

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.

GET/v1/messages

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…"
}
POST/v1/messages/:id/reply

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"
}

Tone profile API

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).

GET/v1/tone-profiles/:inbox_id
POST/v1/tone-profiles/:inbox_id
{
  "formality": 3,           // 1 casual → 5 formal
  "warmth": 4,
  "sentence_length": "short",
  "emoji": "sparingly",
  "sign_off": "— Paul",
  "escalation_topics": ["refunds", "complaints", "custom-quotes"]
}

Webhooks

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 channel
  • message.replied — CLOSER (or human) sent a reply
  • message.escalated — bumped to human queue
  • tone_profile.updated — profile changed
  • integration.disconnected — channel OAuth expired or revoked
POST 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..."
}
Verify the signature. Always. We publish a Node/Python/Ruby helper in our SDK repo — or it's 6 lines of HMAC-SHA256 you can write yourself.

Channel OAuth

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).

  • WhatsApp Business — Meta Cloud API, requires Business Manager admin
  • Instagram DMs — via WhatsApp Business / Meta token
  • LinkedIn — OAuth 2.0, member-level token
  • Email (M365 / Google) — OAuth 2.0, mailbox.read + mailbox.send scopes
  • Email (any IMAP) — app password, stored encrypted
  • SMS — Twilio / MessageBird subaccount or BYOA

Sandbox

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?"
}

Limits & errors

  • Rate limit: 600 requests/minute per token. 429 returned with Retry-After.
  • Payload size: 256 KB. Larger bodies (attachments) use signed upload URLs.
  • Errors: JSON shape { "error": { "code": "...", "message": "...", "trace_id": "..." } }. Always pass us the trace_id if you need support.

What we won't do via API

We don't ship endpoints for things that look like attack surface dressed up as features.
  • No bulk export of other customers' tone profiles — ever.
  • No "send as someone else's number" without that someone's OAuth.
  • No undocumented endpoints. If it's not on this page, it's not in v1.
  • No deprecation without 12 months' notice and a migration script.

Need a signed contract before we issue prod tokens? DPA template, sub-processor list, and SOC2 readiness pack on the Security page.

Get a sandbox token in 2 minutes

Sandbox is free, forever. Wire up the webhooks, fake a few inbound messages, decide if the shape fits your stack — then talk pricing.

📅Book a Demo