Appearance
3. Choose your setup
Make these decisions before writing code. Each row says what to pick and why.
3.1 What kind of calling product are you?
| Your product | Pick | What it means in Calloop |
|---|---|---|
| A support / sales team answering customers (a help desk, a clinic's front desk) | Contact center (steps) | Shared lines with a queue; agents go Available to receive calls; IVR menus, transfers, supervisor whisper and barge |
| A platform serving many businesses, each with its own team | Contact center per business (steps) | One tenant per business, set up by your backend when the business turns calling on |
| Each end user has their own number (an eSIM or virtual-number app, a "business phone in your pocket") | Personal lines (steps) | Each user is an agent who owns one line; a call to the number rings only that user, on all their devices |
| Both (users with their own numbers, plus your own support line) | Both, usually in two tenants | Lines decide: a line with a queue is shared, a line with an owner is personal, and one tenant may hold both. But the settings in §3.4 are per tenant: if end users must not see the board, or your support team needs whisper and barge, put the support team in its own tenant |
| Click-to-call from your CRM (outbound mainly) | Outbound (steps) | Your backend creates the call; the agent's app connects it (§4.7) |
3.2 How many tenants?
| Your product | Pick |
|---|---|
| You serve many separate businesses (multi-tenant SaaS, e.g. a booking platform for salons) | One tenant per business. Each business's agents, lines, queues and calls are isolated. Use your business id as the tenant ref |
| One product whose users are individuals (e.g. an eSIM app) | One tenant for the whole product. Every end user is an agent in it; your internal staff are its supervisors |
3.3 Which channel(s)?
| Channel | Pick it when | You need |
|---|---|---|
| WhatsApp Calling | Your customers already reach you on WhatsApp | A Meta WhatsApp Business number with calling enabled: its phone_number_id and a system-user access token |
| SIP (phone numbers) | Customers dial a normal phone number, or your users need real numbers | A carrier trunk (yours or the operator's) and DIDs on it. The carrier authenticates by IP (no registration) and sends its SDP offer in the INVITE (limits) |
WhatsApp webhook: direct or forwarded?
| Option | Pick it when |
|---|---|
Forward (POST /v1/webhooks/whatsapp/forward with your API key) | Recommended for most products. Your Meta app's webhook already points at your backend (for messages); forward the same payload to Calloop. Calloop only processes calls on your own lines |
Direct (Meta posts to Calloop's /webhooks/whatsapp) | Calloop's operator runs the Meta app and has configured its app secret on Calloop |
3.4 Who may see whom, and listen?
Tenant settings (set with PUT /v1/tenants/{ref}):
| Setting | Contact center | Personal lines |
|---|---|---|
team_view | true: agents see the team board, queues and colleagues' statuses | false: an end user must only ever see their own calls |
monitoring.modes | ["listen", "whisper", "barge"]: supervisors coach and step in | ["listen"]: internal staff may listen, never be heard |
auto_offline | Optional, off by default: { "enabled": true, "missed_threshold": 3 } takes an agent offline after 3 missed offers in a row | Not relevant (personal lines ring the owner regardless of availability) |
Allowing whisper or barge (any mode other than listen) also sends every personal-line call through a media-server room instead of the cheaper direct path (§4.2).
3.5 Recording
Turn it on for your whole account (PUT /v1/recording-policy { "enabled": true }), a tenant (settings.recording.enabled) or one line (recording_enabled: true): the nearest level that chooses wins, and enforced on the account or a tenant stops the levels below from choosing. The file is a stereo Ogg Opus recording (customer left, agent right), stored where the platform's operator chose (Calloop's servers or their S3 bucket). A personal line's call keeps the cheap direct path when it is recorded: copies of both sides are recorded, the call itself is untouched. recording.completed says when a recording is ready, recording.failed that a call won't have one. Fetch it with a short-lived URL (GET /v1/calls/{id}/recording). Old recordings move to cheaper storage and are deleted on the platform's schedule, or the tenant's own: settings.recording.cold_after_days and settings.recording.retention_days.
3.6 Which client SDK?
| Your app | Use | Install |
|---|---|---|
| Web app (any framework) | JS SDK @calloop/agent-sdk (README) | Not on npm yet: depend on it from the Calloop repo (sdk/agent-js) |
| React web app | React bindings @calloop/agent-react on top of the JS SDK (README) | Same: from the repo (sdk/agent-react) |
| Flutter app (Android / iOS) | Dart SDK calloop_agent (README), with native_calls.dart for the phone's own call screen (Android only; on iOS your app handles PushKit/CallKit, see §7) | Not on pub.dev: a git or path dependency on sdk/agent-dart |
| Anything else (native iOS/Android, desktop) | Implement the protocol directly: Socket.IO + WebRTC (agent-protocol.md) | — |
| A server-side bot or test | The JS SDK in Node, with @roamhq/wrtc for WebRTC | From the repo |
3.7 Push
| Platform | What Calloop sends | Credentials you give Calloop |
|---|---|---|
| Android | FCM data message (call.incoming, call.cancelled) | Firebase service account JSON |
| iOS | APNs VoIP push (PushKit → CallKit), plus an FCM banner | APNs .p8 key, key id, team id, bundle id |
| Web | OneSignal | OneSignal app id and REST key |
You register each device's token from your backend (PUT …/agents/{ref}/push-tokens/{deviceId}). A push never carries call details beyond ids and the caller: the app connects and syncs.