Authentication
The Firetell API supports two authentication methods, each designed for a different use case:
| Method | Scheme | Use Case |
|---|---|---|
| API Key | ApiKey sk-... | Server-to-server calls from your backend |
| JWT Token | Bearer <jwt> | Client-side calls from your web or mobile app |
Both methods use the Authorization HTTP header.
API Key Authentication (Server-side)
Use API Keys to authenticate server-to-server requests from your backend. API keys start with the sk- prefix and are passed using the ApiKey scheme.
Authorization: ApiKey sk-your_api_key
Do NOT use API Keys in client-side code. API Keys grant full access to your workspace and must never be embedded in frontend applications — including JavaScript bundles, mobile apps, or any code that runs on the user's device. Attackers can easily extract keys from client-side code and use them to read, modify, or delete your workspace resources.
For client-side authentication, use JWT Tokens instead.
Quick Example
curl -X GET "https://{workspace_id}.firetell.app/api/v1/phone-numbers" \
-H "Authorization: ApiKey sk-your_api_key"
Getting Your API Key
- Log in to the Firetell Console
- Select your workspace
- Navigate to Settings → API Keys
- Click Create API Key
- Enter a descriptive name
- Copy the generated key (starts with
sk-)
Your API Key is only shown once at creation time. Store it securely — you will not be able to retrieve it again. If lost, delete the key and create a new one.
Each workspace can have up to 100 API keys. Use descriptive titles like "Production Server", "Staging", or "CRM Integration" to organize your keys.
Code Examples
cURL
curl -X GET "https://{workspace_id}.firetell.app/api/v1/phone-numbers" \
-H "Authorization: ApiKey sk-your_api_key" \
-H "Content-Type: application/json"
Node.js
const response = await fetch(
"https://{workspace_id}.firetell.app/api/v1/phone-numbers",
{
headers: {
Authorization: "ApiKey sk-your_api_key",
"Content-Type": "application/json",
},
},
);
const data = await response.json();
Python
import requests
response = requests.get(
'https://{workspace_id}.firetell.app/api/v1/phone-numbers',
headers={
'Authorization': 'ApiKey sk-your_api_key',
'Content-Type': 'application/json',
},
)
data = response.json()
JWT Authentication (Client-side)
Use JWT tokens to authenticate client-side requests from your web or mobile application. Your backend signs JWTs using the API Key secret (sk-...) with the HS256 algorithm.
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
How It Works
- Your backend signs a JWT using your API Key secret (
sk-...) with HS256 - Your backend returns the JWT to the client application
- The client includes the JWT in all API requests
┌──────────┐ ┌──────────────┐
│ Client │──(1)──▶ │ Your Backend │
│ (Web / │ │ Signs JWT │
│ Mobile) │◀──(2)── │ with sk-... │
│ │ └──────────────┘
│ │
│ │──(3)──────────────────────────▶ Firetell API
│ │ Authorization: (Bearer JWT)
└──────────┘
- Client requests a token from your backend
- Your backend signs a JWT locally using the API Key secret and returns it
- Client calls Firetell API directly with
Bearer <jwt>
JWT Claims
| Claim | Description | Example |
|---|---|---|
iss | API Key SID | sid-your_api_key_sid |
sub | Subject identifier (see Audience Types below) | agent_123 or user_abc |
aud | Audience type: agent-api or client-api | agent-api |
exp | Expiration timestamp | 24 hours from now |
domain | Workspace domain (custom claim) | yourcompany.firetell.app |
call_flow_id | Visual Call Flow ID (custom claim, required for client-api) | cf_abc123xyz789000 |
Audience Types
The aud claim determines what type of identity the token represents. Firetell supports two distinct identities:
| Audience | sub requirement | Use case |
|---|---|---|
agent-api | Must be the username of an existing, active agent in your workspace | Call center agents, support reps |
client-api | Can be any identifier you define (e.g. user ID, phone number, email) | End-user clients, in-app calling |
Agent API vs Client API
Understanding the difference between these two identities is key to building the right integration.
Agent API (agent-api)
Agents are managed users created by workspace admins via Console or the Agents API. They represent real people in your organization (support reps, sales agents, operators).
| Feature | Details |
|---|---|
| Created by | Workspace admin (Console or API) |
| Username | Fixed, unique within workspace |
| Quantity | Limited by your workspace plan |
| PSTN calls | ✅ Outbound and inbound via phone numbers |
| Call routing | ✅ Queues, IVR, team-based routing |
| Internal calls | ✅ Agent ↔ Agent, Agent ↔ Client |
| Call Center Portal | ✅ Web, iOS, Android app |
| Presence | ✅ Online/offline/busy state |
| Teams | ✅ Assigned to teams for routing |
Agent Limits
The number of agents you can create depends on your workspace plan. To view your current limits: Console → Workspace Settings → Limits.
Need more agents? Upgrade your plan or contact enterprise@firetell.com.
Device Session Limit
For agents logging in via the official portal endpoint (/auth/login), each agent is allowed a maximum of 5 concurrent active device sessions (e.g., 1 browser, 1 mobile app, 1 desktop app, etc.).
If an agent logs in on a 6th device, the oldest session is automatically evicted (logged out) to enforce limits.
Note: If your backend signs JWT tokens manually (custom integration) without providing a device_id claim, this 5-device limit check is bypassed.
Client API (client-api)
Clients are dynamic identities — your backend signs a JWT with any sub value you choose. No pre-registration needed. Clients are designed for in-app, internal VoIP calling between your users.
| Feature | Details |
|---|---|
| Created by | Your backend signs JWT with any username |
| Username | Any identifier (user ID, phone, email, session) |
| Quantity | Unlimited tokens (limit on active connections) |
| PSTN calls | ❌ Internal VoIP only |
| Call routing | ❌ Direct calls only |
| Internal calls | ✅ Client ↔ Client, Client ↔ Agent |
| Call Center Portal | ❌ Integrates into your own app |
| Presence | ✅ Online/offline state |
| Teams | ❌ Not applicable |
Concurrent Connection Limit
While you can generate and sign unlimited client JWT tokens (client-api), there is a default workspace limit of 1,000 concurrent active WebSocket signaling connections.
If your application requires higher concurrent limits, please contact enterprise@firetell.com to increase the limit for your workspace.
Use Case Examples
| Scenario | Identity | Flow |
|---|---|---|
| Ride-hailing | client-api | Driver app ↔ Customer app (in-app VoIP) |
| E-commerce support | Both | Customer (client-api) → Call center agent (agent-api) |
| Telemedicine | client-api | Patient app ↔ Doctor app |
| Customer support | agent-api | Inbound PSTN call → IVR → Agent |
| Sales outreach | agent-api | Agent → Outbound PSTN call to lead |
| In-app calling | client-api | User A ↔ User B within your app |
┌─────────────────────────────────────────────────────────┐
│ Your Application │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Client API │ ◄── VoIP ──► │ Client API │ │
│ │ (Driver) │ │ (Customer) │ │
│ └──────────────┘ └──────┬───────┘ │
│ │ │
│ VoIP │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Agent API │ │
│ │ (Support) │ │
│ └──────┬───────┘ │
│ │ │
└──────────────────────────────────────────┼───────────────┘
│
PSTN │
▼
┌─────────────┐
│ Phone │
│ Network │
└─────────────┘
Quick Examples
Agent API token
const jwt = require("jsonwebtoken");
const token = jwt.sign(
{ domain: "yourcompany.firetell.app" },
"sk-your_api_key_secret",
{
algorithm: "HS256",
expiresIn: "24h",
issuer: "sid-your_api_key_sid",
subject: "agent_123", // Must be an existing, active agent username
audience: "agent-api",
},
);
Client API token
const jwt = require("jsonwebtoken");
const token = jwt.sign(
{
domain: "yourcompany.firetell.app",
call_flow_id: "cf_your_call_flow_id", // Required for client-api to route calls to a Call Flow
},
"sk-your_api_key_secret",
{
algorithm: "HS256",
expiresIn: "24h",
issuer: "sid-your_api_key_sid",
subject: "user_abc", // Any custom identifier you define
audience: "client-api",
},
);
For detailed examples in Python, PHP, and more, see the Client Authentication guide.
JWT tokens are designed for client-side usage and expire after a short period. Always sign tokens on your backend and pass them to the client — never expose your API Key secret to client-side code.
Workspace Scope
Both API keys and JWT tokens are scoped to a single workspace. The workspace is determined by the subdomain in the request URL:
https://{workspace_id}.firetell.app/api/v1/...
^^^^^^^^^^^^^^
workspace scope
You cannot access resources from other workspaces with a single key or token.
Choosing the Right Method
API Key (ApiKey) | JWT Token (Bearer) | |
|---|---|---|
| Where to use | Backend / server | Frontend / client |
| Lifetime | Long-lived (until revoked) | Short-lived (expires) |
| Security | Must be kept secret | Safe to send to client |
| Access level | Full workspace access | Scoped by JWT claims |
| Example | REST API integrations, webhooks, cron jobs | WebRTC, real-time apps |
Never expose API Keys in client-side code. API keys grant full access to your workspace. Use JWT tokens for any code that runs in the browser or on mobile devices.
Use separate API keys for each authentication flow. Create one key for server-to-server API calls (ApiKey scheme) and a different key for signing client JWT tokens (Bearer scheme). This makes it easy to identify which flow is affected if a key is compromised, and you can revoke a single key without disrupting the other.
Rate Limiting
API requests are rate-limited to protect service quality:
| Tier | Requests per minute |
|---|---|
| Default | 100 |
| Auth | 10 |
| Heavy | 30 |
| Enterprise | Custom |
When rate limited, you'll receive a 429 Too Many Requests response with a Retry-After header. Implement exponential backoff in your retry logic.
See Rate Limiting for full details.
Error Responses
Missing Authorization Header
{
"statusCode": 401,
"message": "Missing Authorization header"
}
Unsupported Scheme
{
"statusCode": 401,
"message": "Unsupported authorization scheme \"Basic\". Use \"Bearer\" or \"ApiKey\""
}
Invalid API Key
{
"statusCode": 401,
"message": "Invalid API Key"
}
Invalid or Expired JWT
{
"statusCode": 401,
"message": "Unauthorized"
}
Security Best Practices
- Never expose API keys in client-side code — Use them only on your backend server
- Use JWT tokens for clients — Generate short-lived tokens for browser and mobile apps
- Rotate keys regularly — Delete old keys and create new ones periodically
- Use separate keys per environment — Different keys for development, staging, and production
- Store secrets securely — Use environment variables or a secret manager, never hardcode in source code
Next Steps
- Quickstart — Make your first API call
- API Reference — Explore all available endpoints