Skip to content

Onboarding ​

What any product does to adopt Talkline, in order, and how to know each step worked before you take the next. Each step's check is something you can run yourself; none of them needs anyone at Talkline.

These pages use https://api.talkline.example for the control API and wss://signal.talkline.example for the signaling server. Use the two addresses your deployment's operator gives you, and keep them in your backend's configuration beside the key and secret of step 1. These pages name the four TALKLINE_API_URL, TALKLINE_SIGNALING_URL, TALKLINE_API_KEY and TALKLINE_API_SECRET. Only your backend calls the control API. Your web client needs the signaling address alone, and learns it the way it learns the rest of its configuration: built into it, or in an answer from your backend when it starts. One build of your client then runs against any deployment.

1. Get an API key ​

The operator of your Talkline deployment registers your application and gives you an API key and an API secret. The key (tlk_live_…) names your application; the secret proves it. The secret is shown once, when the key is created — store it the way you store your database password, on your backend only.

Every control API request carries both, joined by a dot:

http
Authorization: Bearer tlk_live_7f2c9a….<api_secret>

Check

GET /v1/health with your key answers 200.

bash
curl -s $TALKLINE_API_URL/v1/health \
  -H "Authorization: Bearer $TALKLINE_API_KEY.$TALKLINE_API_SECRET"
# {"status":"ok","database":"ok","redis":"ok"}

A 401 is a wrong key or secret. (The /v1/applications routes are the operator's and refuse your key — that is expected.)

2. Mint access tokens on your backend ​

Your clients never see your API secret. Instead your backend gives each signed-in user a short-lived access token naming who they are in Talkline — an identity of your choosing, such as app:user:123 — and what they may do. It is a JWT you sign with your API secret, in your own process: no network call to Talkline.

Add a token endpoint to your backend that checks the user's own session and returns a fresh token. In Node, @talkline/server-sdk mints it (Installing the SDKs):

ts
import express from 'express';
import { Talkline } from '@talkline/server-sdk';

const talkline = new Talkline({
  apiKey: process.env.TALKLINE_API_KEY!,
  apiSecret: process.env.TALKLINE_API_SECRET!,
  baseUrl: process.env.TALKLINE_API_URL!, // https://api.talkline.example
});

const app = express();

app.post('/api/talkline-token', requireSignedIn, async (req, res) => {
  const token = await talkline.mintToken({
    identity: `app:user:${req.user.id}`,
    name: req.user.displayName,
    ttlSec: 300,
    grants: {
      roomJoin: true,
      roomCreate: true, // placing a call creates a room
      canPublish: true,
      canSubscribe: true,
      canPublishData: true,
      canPublishSources: ['microphone', 'camera'],
    },
  });
  res.json({ token });
});

A token is single-use and lives at most ten minutes. The SDK calls your endpoint again for every connect and reconnect, so never cache one. Access tokens has the claims, the grants and how to mint them from any language.

Check

POST /v1/tokens/introspect verifies a token exactly as the signaling server will, and answers with the identity and the grants Talkline would give it.

bash
curl -s $TALKLINE_API_URL/v1/tokens/introspect \
  -H "Authorization: Bearer $TALKLINE_API_KEY.$TALKLINE_API_SECRET" \
  -H 'content-type: application/json' \
  -d "{\"token\": \"$TOKEN\"}"

Introspection spends the token, as a connect would: introspect one token, and connect with another.

3. Receive webhooks ​

Register an https endpoint on your backend. Talkline signs every delivery with the endpoint's secret and delivers at least once, so your handler verifies the signature over the raw body and does its work once per delivery id.

ts
const endpoint = await talkline.webhooks.create({
  url: 'https://app.example.com/talkline/webhooks',
  events: ['call.ended', 'call.missed'],
});
// endpoint.secret — whsec_… — is in this answer and never again. Store it now.
ts
import { parseWebhook, type WebhookDelivery } from '@talkline/server-sdk';

app.post('/talkline/webhooks', express.raw({ type: 'application/json' }), async (req, res) => {
  let delivery: WebhookDelivery;
  try {
    delivery = parseWebhook(process.env.TALKLINE_WEBHOOK_SECRET!, req.body, req.get('x-talkline-signature'));
  } catch {
    return res.sendStatus(400); // not signed by Talkline with this secret, or too old
  }
  if (await alreadyProcessed(delivery.id)) return res.sendStatus(204);

  if (delivery.event === 'call.ended') {
    await recordCall(delivery.data.callSid, delivery.data.durationSec, delivery.data.billableSec);
  }
  await markProcessed(delivery.id);
  res.sendStatus(204);
});

call.ended is the event your billing reads: it arrives once for every call that was answered, with its duration and billable seconds — and also for a call whose room ended while it was still ringing, with both of them null. Webhooks covers signing, retries, testing against a deployment on your own machine, and every event.

Check

Place a call from your backend to an identity nobody is signed in as. It rings nobody, ends unanswered, and Talkline sends call.missed:

bash
curl -s $TALKLINE_API_URL/v1/calls \
  -H "Authorization: Bearer $TALKLINE_API_KEY.$TALKLINE_API_SECRET" \
  -H 'content-type: application/json' \
  -d '{"from": "test:caller", "to": ["test:nobody"], "ringTimeoutSec": 5}'

Your handler should process exactly one delivery for that call. The endpoint's delivery log shows what Talkline sent and what your endpoint answered: GET /v1/webhooks/{id}/deliveries.

4. Sync identities ​

An identity is any string you choose, up to 200 characters: app:user:{id} for your users, agent:{id} for support staff, guest:{uuid} for someone without an account. Talkline never resolves it to a person.

When a user signs in or changes their profile, tell Talkline their display name and their availability:

ts
await talkline.identities.upsert(`agent:${agent.id}`, { displayName: agent.name, availability: 'available' });

Availability is what a person chose — available, away or dnd — and queues only offer calls to available people. Presence is what Talkline sees: whether the identity has a connected client right now. You set the first; the second follows from your clients being connected. Identities and presence has more.

Check

With your agents signed in to your app, GET /v1/presence/online lists them.

5. Ship the SDK behind a flag ​

Add @talkline/core (and @talkline/react in a React app) to your client — The web SDK — and build the calls your product needs: one-to-one, a group room, a queue.

Release it behind your own per-user feature flag, with a kill switch you can turn off instantly, and turn it on for internal users first.

Check

Internal users make real calls for a week. Every call they make shows up in GET /v1/calls, and every answered one reached your webhook as call.ended exactly once — once per endpoint, if you registered more than one.

6. Roll out ​

Turn the flag on for 1 %, then 10 %, 50 % and 100 % of users, and flip the kill switch deliberately at the first step to prove it works. Rolling out lists the measures to watch at each step.

Two shapes this fits ​

A private call between two people. Your backend mints a token for each side, scoped to a room it names. Neither side learns the other's real identity: the identity is opaque and the display name is yours to choose. A limit on how many calls a user may make is your rule; Talkline meters usage, not relationships.

A support desk. Agents have identities like agent:7; customers are guest:{uuid} with no account — no special case for either. Topics become queue skills, and an agent stepping away is a paused queue membership. Bringing in a colleague is a second invite into the same room.

What is not provided ​

  • No compatibility layer for a calling protocol you used before. Your clients move to the Talkline SDKs.
  • No data import. Talkline starts empty; your call history from before stays where you kept it.
  • No parallel run for one user. A user is on your previous path or on Talkline, never both at once.

Talkline documentation