Appearance
Scenarios: which one are you?
Every product that uses Calloop is one of a few shapes. Find yours below and follow its page: each one lists, in order, the steps for that kind of product, marks every step Required or Optional, and shows the actual requests. The integration guide explains the concepts behind them; the Management API, events and agent protocol references have every field.
| Your product | Example | Scenario |
|---|---|---|
| One business's team answers customers who call a WhatsApp number or a phone number | A company's support desk, a clinic's front desk, a sales team | Contact center |
| A platform serving many businesses, each with its own team and numbers | A helpdesk, CRM or messaging SaaS adding calls for its customers | Multi-business platform |
| Each of your users has their own number, rings only them and calls out from it | An eSIM app, a virtual-number or second-line app | Personal numbers |
| Your users call out to contacts from your product, mostly outbound | A CRM's "Call" button, a sales dialer, appointment reminders by a person | Click-to-call |
Mixed products. Pick the closest page and add the pieces you need from another: a personal-number app with its own support line follows Personal numbers and adds a queue and a shared line from Contact center, ideally in a separate tenant (so its end users never see the support team's board). A contact center that also calls customers back adds the outbound steps from Click-to-call.
What every integration has
Whatever the scenario, these pieces are the same. Each scenario page points back here.
| Piece | Required? | What it is |
|---|---|---|
| Your consumer and its keys | Required | The Calloop operator creates your consumer and sends you, once, an API key (your backend), a signing key (kid + secret, for agent tokens) and, if they set a webhook or lookup URL for you, a webhook secret (otherwise the first PUT /v1/webhook that sets a URL returns it, once). Keep them in a secrets manager |
| A tenant | Required | PUT /v1/tenants/{ref}: a workspace (one business, or your whole product) |
| Agents | Required | PUT /v1/tenants/{t}/agents/{ref}: the people who take calls (your user id as the ref) |
| At least one line | Required | PUT /v1/tenants/{t}/channels/{ref}: a WhatsApp number or a phone number (DID) on a SIP trunk |
| A token endpoint | Required | Your backend signs a short-lived JWT for the logged-in user; the app connects with it |
| The agent app | Required | Your app on the Calloop SDK (JavaScript, React or Flutter): rings, answers, calls out |
| The webhook | Recommended | PUT /v1/webhook: Calloop tells your backend what happened (call.ended for history, billing, CRM) |
| Push credentials and device tokens | Required for mobile apps | So a phone in the background rings; not needed for a browser-only app |
| Everything else | Optional | IVR, overflow, supervisors, recording, reports, the lookup hook, the dashboard |
The token endpoint (every scenario)
Your app never holds a key: your backend signs a token for the user who is logged in, for their own tenant, valid a few minutes. The SDK fetches a fresh one on every reconnect.
js
// Node (jsonwebtoken) — GET /api/calloop-token
const token = jwt.sign(
{ iss: 'yourproduct', tnt: tenantRef, sub: user.id, name: user.name, role: 'AGENT' },
process.env.CALLOOP_SIGNING_SECRET,
{ algorithm: 'HS256', keyid: process.env.CALLOOP_SIGNING_KID, expiresIn: '10m' },
);role is AGENT or SUPERVISOR. Calloop creates the agent on first connect if you haven't, but creating agents ahead with PUT is better: queues and lines can name them before they sign in. The token's name and role update the agent on every connect, so make them agree with your PUT: always send name (without it the agent is renamed to its ref), and sign the same role (a role in the token replaces the one you PUT). More, including PHP: integration guide → Sign agent tokens.
The webhook (every scenario)
http
PUT /v1/webhook
{ "url": "https://api.example.com/calloop/events",
"event_types": ["call.created", "call.answered", "call.ended"] }The first time a URL is set (if the operator didn't send you a webhook secret), the response returns { "webhook": { … }, "secret": "…" }: the secret, once — store it. The PUT replaces all your webhook settings: a URL you leave out (url, lookup_url) is removed, and no event_types means every type.
Then, on every request: verify the X-Calloop-Signature header, answer 2xx at once, process afterwards, and ignore an event_id you have already seen (delivery is at least once) (events → Verifying requests). call.ended alone is enough for history and billing; it carries the whole call.
Rules for every API call
Authorization: Bearer <API key>on every request, from your backend only.PUTcreates or replaces: send the whole object every time.- Send an
Idempotency-Keyheader (a new UUID per action) on everyPOST, so a retry never places a second call. - Errors are always
{ "error": { "code", "message" } }; at most 1200 requests a minute.