Skip to content

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 ​

#StepRequired?When
1Get your keysRequiredonce
2Receive events and connect WhatsAppRequiredonce
3Token endpointRequiredonce
4A business turns calling onRequiredper business
5Keep agents in syncRequiredas your users change
6The agent appRequiredonce
7Per-business optionsOptionalper business
8Give businesses their own setup screensOptionalonce
9Suspend or remove a businessRequired (have it ready)per business
10Test before launchRequiredonce

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 productIn Calloop
A user gets calling accessPUT /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 teamPUT /v1/tenants/biz-123/queues/main/members with the new full list
They lose access or leaveDELETE /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:

OptionWhere
IVR menuContact center → IVR menu
Overflow, waits, hold musicContact center → Overflow
Recording, and how long to keep ityour 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 coachingthe tenant's settings.monitoring.modes
Caller names from the business's contactsthe lookup hook: Calloop sends tenant_ref with each caller, so you look in the right business's contacts
ReportsGET /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-123 with "status": "SUSPENDED" (and its name): its agents can't connect. Also PUT each 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.ended arrives with the right tenant_ref and is filed under the right business
  • A removed user is disconnected; a suspended business's agents can't connect

Calloop — the call engine behind your product.