Appearance
Scenario: a multi-business platform
For a platform that serves many businesses: a helpdesk, CRM or messaging product adding calls for its customers. Each business on your platform has its own team, its own numbers (usually its WhatsApp number) and its own calls, isolated from every other business. You set each one up automatically when it turns calling on.
This is the Contact center scenario, repeated once per business and driven by your backend. Read that page for the per-business pieces (queues, IVR, the agent app, supervisors); this page covers what changes when there are many.
At the end you have: a calling feature any business on your platform can switch on, with agents answering in your app, each business seeing only its own calls, and the history in your own database.
Steps at a glance
| # | Step | Required? | When |
|---|---|---|---|
| 1 | Get your keys | Required | once |
| 2 | Receive events and connect WhatsApp | Required | once |
| 3 | Token endpoint | Required | once |
| 4 | A business turns calling on | Required | per business |
| 5 | Keep agents in sync | Required | as your users change |
| 6 | The agent app | Required | once |
| 7 | Per-business options | Optional | per business |
| 8 | Give businesses their own setup screens | Optional | once |
| 9 | Suspend or remove a business | Required (have it ready) | per business |
| 10 | Test before launch | Required | once |
Every request carries Authorization: Bearer <your API key>. The business on your platform with id biz-123 is the tenant biz-123.
1. Get your keys
Required, once. The Calloop operator creates your consumer; you receive the API key, the signing key and, if the operator set a webhook or lookup URL for you, the webhook secret once (otherwise your first PUT /v1/webhook with a URL returns it, once — store it) (what every integration has). One consumer serves all your businesses.
2. Receive events and connect WhatsApp
Required, once.
Webhook. One endpoint receives the events of every business; each event carries tenant_ref (your business id), so you file it under the right business (how).
http
PUT /v1/webhook
{ "url": "https://api.example.com/calloop/events",
"event_types": ["call.created", "call.answered", "call.ended", "recording.completed"] }WhatsApp. Your Meta app's webhook receives every business's WhatsApp traffic (messages and calls) at your backend. Forward each payload to Calloop, unchanged, from that same handler:
js
// In your existing Meta webhook handler, after verifying Meta's signature:
await fetch('https://api.calloop.net/v1/webhooks/whatsapp/forward', {
method: 'POST',
headers: { Authorization: `Bearer ${CALLOOP_API_KEY}`, 'Content-Type': 'application/json' },
body: rawBody, // the same JSON Meta sent you
});Calloop picks out the call events for numbers you have registered as lines and ignores the rest (messages, other numbers), so you can forward everything.
3. Token endpoint
Required, once. The same endpoint for all businesses: tnt is the logged-in user's own business, never one taken from the request.
js
const token = jwt.sign(
{ iss: 'yourplatform', tnt: user.businessId, sub: user.id, name: user.name,
role: user.isManager ? 'SUPERVISOR' : 'AGENT' },
process.env.CALLOOP_SIGNING_SECRET,
{ algorithm: 'HS256', keyid: process.env.CALLOOP_SIGNING_KID, expiresIn: '10m' },
);4. A business turns calling on
Required, per business. When a business enables calls (or connects its WhatsApp number in your onboarding), your backend runs these in order. All are PUTs, so running them again is harmless — except for a suspended business: a tenant PUT without status sets it back to ACTIVE, so send its current status (or don't rerun them while it's suspended).
http
# 1. The tenant: one per business, your business id as the ref
PUT /v1/tenants/biz-123
{ "name": "Acme Ltd", "settings": { "team_view": true, "monitoring": { "modes": ["listen", "whisper", "barge"] } } }
# 2. Its agents: the business's users who take calls
PUT /v1/tenants/biz-123/agents/user-17 { "name": "Rana", "role": "AGENT" }
PUT /v1/tenants/biz-123/agents/user-2 { "name": "Omar", "role": "SUPERVISOR" }
# 3. A queue with them in it
PUT /v1/tenants/biz-123/queues/main { "name": "Main", "strategy": "ROUND_ROBIN", "ring_timeout_seconds": 20 }
PUT /v1/tenants/biz-123/queues/main/members
{ "members": [{ "agent_ref": "user-17" }, { "agent_ref": "user-2" }] }
# 4. Its WhatsApp number, routed into the queue
PUT /v1/tenants/biz-123/channels/wa-main
{ "type": "WHATSAPP", "address": "+442079460000",
"provider_account_id": "<the business's phone_number_id>",
"credentials": { "access_token": "<token with calling permission>" },
"inbound_queue_ref": "main" }The phone_number_id and token come from however the business connected WhatsApp to your platform (for example Meta's embedded signup). A business may have several numbers: one line each, with its own channel_ref.
5. Keep agents in sync
Required, as your users change. Mirror your users into Calloop:
| In your product | In Calloop |
|---|---|
| A user gets calling access | PUT /v1/tenants/biz-123/agents/user-17 |
| Their role changes (manager) | PUT again with "role": "SUPERVISOR", and make your token endpoint sign role: 'SUPERVISOR' for them too (the token's name and role update the agent on every connect) |
| They join or leave a team | PUT /v1/tenants/biz-123/queues/main/members with the new full list |
| They lose access or leave | DELETE /v1/tenants/biz-123/agents/user-17: disconnected at once, tokens refused |
6. The agent app
Required, once. One app for all businesses: the same SDK code as the Contact center agent app. Each agent only ever sees their own business's calls and board, because their token names their tenant.
7. Per-business options
Optional, per business. Everything from the Contact center page works per tenant, so you can offer it as a business's settings:
| Option | Where |
|---|---|
| IVR menu | Contact center → IVR menu |
| Overflow, waits, hold music | Contact center → Overflow |
| Recording, and how long to keep it | your account's default (/v1/recording-policy), the tenant's settings.recording.enabled, or the line's recording_enabled; the tenant's settings.recording.retention_days |
| Supervisors listening and coaching | the tenant's settings.monitoring.modes |
| Caller names from the business's contacts | the lookup hook: Calloop sends tenant_ref with each caller, so you look in the right business's contacts |
| Reports | GET /v1/tenants/biz-123/reports/calls, …/agents, …/live |
Remember: a tenant's PUT keeps its settings when you leave them out (but not its status: left out, it becomes ACTIVE), an agent's keeps its role and a line's keeps its credentials; otherwise a line's or queue's PUT replaces every field.
8. Give businesses their own setup screens
Optional, once. Instead of building screens for queues, IVR, the live board and reports, send a business's admins into Calloop's dashboard, signed in by your platform with no Calloop password: your backend signs a short launch token for the user and their business and redirects them (Dashboard launch).
9. Suspend or remove a business
Required to have ready.
- Suspend (unpaid, paused):
PUT /v1/tenants/biz-123with"status": "SUSPENDED"(and itsname): its agents can't connect. AlsoPUTeach of its lines with"status": "DISABLED", so calls to them are refused instead of waiting for agents who can't come."ACTIVE"on both restores it. - Leave calling: delete its lines (
DELETE …/channels/wa-main) so its number stops routing to Calloop, and its agents. - Remove it for good (with its call history and recordings): suspend it, then
DELETE /v1/tenants/biz-123. Refused while a call is in progress; can't be undone. - A customer of the business asks to be forgotten:
POST /v1/tenants/biz-123/customers/erase { "address": "+447700900123" }.
10. Test before launch
Required.
- Onboard a test business end to end with your automation (step 4), twice: the second run changes nothing
- A WhatsApp call to its number, forwarded from your Meta webhook, rings its agent
- An agent of business A never sees business B's calls, board or events
call.endedarrives with the righttenant_refand is filed under the right business- A removed user is disconnected; a suspended business's agents can't connect