---
sidebar_position: 2
title: Authentication
description: How to authenticate with the Firetell API using API Keys or JWT tokens.
---

# 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
```

:::danger

**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](#jwt-authentication-client-side) instead.

:::

### Quick Example

```bash
curl -X GET "https://{workspace_id}.firetell.app/api/v1/phone-numbers" \
  -H "Authorization: ApiKey sk-your_api_key"
```

### Getting Your API Key

1. Log in to the [Firetell Console](https://console.firetell.com)
2. Select your workspace
3. Navigate to **Settings** → **API Keys**
4. Click **Create API Key**
5. Enter a descriptive name
6. Copy the generated key (starts with `sk-`)

:::caution
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.
:::

:::tip
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

```bash
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

```javascript
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

```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

1. Your **backend** signs a JWT using your API Key secret (`sk-...`) with HS256
2. Your backend returns the JWT to the **client** application
3. 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)
└──────────┘
```

1. Client requests a token from your backend
2. Your backend signs a JWT locally using the API Key secret and returns it
3. 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](https://console.firetell.com) or the [Agents API](/docs/rest-api/workspace-api/agents). 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          |

:::info
**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](mailto:enterprise@firetell.com).
:::

:::note
**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                               |

:::caution
**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](mailto: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

```javascript
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

```javascript
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](/docs/sdks/javascript/authentication) guide.

:::tip
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 |

:::warning
**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.
:::

:::tip
**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](/docs/rest-api/overview#rate-limiting) for full details.

---

## Error Responses

### Missing Authorization Header

```json
{
  "statusCode": 401,
  "message": "Missing Authorization header"
}
```

### Unsupported Scheme

```json
{
  "statusCode": 401,
  "message": "Unsupported authorization scheme \"Basic\". Use \"Bearer\" or \"ApiKey\""
}
```

### Invalid API Key

```json
{
  "statusCode": 401,
  "message": "Invalid API Key"
}
```

### Invalid or Expired JWT

```json
{
  "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](/docs/getting-started/quickstart) — Make your first API call
- [API Reference](/docs/rest-api/overview) — Explore all available endpoints
