Concept
Authentication & API keys
Every request to the TelVox Connect REST API is authenticated with an API key. Browser and mobile clients that place or receive calls over WebRTC use a different credential — a short-lived access token your server mints. This page covers both, and how to keep your secret out of the wrong places.
Connect is in developer preview. The auth model below — bearer API keys and short-lived WebRTC access tokens — reflects how the platform works today, but exact field names, header conventions and self-serve key issuance may change before general availability. During the preview, sandbox credentials are provisioned with the TelVox team.
API keys: a SID and a secret
An API key is a pair: a key SID that publicly identifies the key, and a secret that authorizes it. The SID is safe to log and reference; the secret is a password and is shown to you exactly once, at creation. TelVox stores secrets encrypted at rest with AES-256-GCM and never displays them again — if you lose a secret, you rotate the key.
# illustrative — shape may differ at GA
# An API key is a SID + secret pair. The SID names the key;
# the secret authorizes it. Store the secret like a password.
TELVOX_API_KEY_SID=SKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # public identifier
TELVOX_API_SECRET=•••••••••••••••••••••••••••••••• # shown ONCE at creationConceptually a key has a scope. A standard key can act across the resources your organization can reach; a restricted key is narrowed to specific resources and verbs, so you can hand a billing job a read-only key without giving it the ability to place calls. Prefer the narrowest key that does the job.
Authorize a request
Send the key as a bearer token in the Authorization header. Read it from an environment variable rather than hard-coding it — the SDKs default to TELVOX_API_KEY.
curl https://api.telvox.dev/v1/calls \
-H "Authorization: Bearer $TELVOX_API_KEY" \
-d from="+14155550100" \
-d to="+14155550199" \
-d answer_url="https://your.app/voice/answer"The API key secret is a server credential. Do not embed it in a browser bundle, a mobile app, or any code that ships to a device — anyone who can read it can place calls on your account. All REST calls that carry the key must originate from your backend. Browser and mobile clients use short-lived access tokens instead.
Rotation & revocation
Keys are independent credentials, so you rotate without downtime: create a new key, deploy it to your environment, confirm traffic is flowing on the new SID, then revoke the old one. Because the SID is part of every authenticated request, you can attribute usage per key and revoke a single leaked credential without touching the rest of your fleet.
- Rotate on a schedule, and immediately if a secret is exposed in logs, a repo, or a screen-share.
- Use distinct keys per environment and per service so revocation has a small blast radius.
- Revoking a key takes effect immediately — in-flight calls are unaffected, but no new requests authorize on the revoked SID.
Access tokens for WebRTC
A browser or mobile client can't hold your API key — so it can't use one. Instead, your server uses its API key to mint a short-lived JWT access token carrying a voice grant, and hands that token to the client. The client presents the token to register its WebRTC endpoint and place or receive calls. The token is signed, scoped to a single identity, and expires in minutes — so a leaked token is low-risk and self-healing.
This is a real, shipped pattern: TelVox already issues short-lived JWTs (access tokens measured in minutes, with single-use refresh rotation) for its WebRTC softphone, and signing secrets are encrypted at rest with AES-256-GCM. The minting endpoint itself is part of the developer preview.
// SERVER-SIDE ONLY — uses your API key, returns a short-lived JWT.
app.post("/voice/token", requireSession, async (req, res) => {
const { token, expiresAt } = await client.accessTokens.create({
identity: req.user.id, // who the token represents
grants: { voice: { incoming: true, outgoing: true } },
ttl: 600, // seconds — keep it short
});
res.json({ token, expiresAt }); // hand the JWT to the browser/mobile client
});The response is the token the client consumes, plus its expiry. Re-mint before expiry rather than lengthening the TTL.
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ...",
"identity": "user_8f3a",
"grants": { "voice": { "incoming": true, "outgoing": true } },
"expires_at": "2026-06-22T18:14:00Z",
"ttl": 600
}In short
- Server-to-server REST → API key (SID + secret) as a bearer token. Secret stays on the server.
- Browser / mobile WebRTC → short-lived JWT access token, minted server-side from your API key.