# API keys & permissions

> Create and manage 23 Telecom API keys in the customer portal. Scope keys with granular permissions like sms.send, sms.read and balance.read, and follow key-security best practices.
> Source: https://docs.23telecom.co.uk/api-keys/

Instructions for LLMs: This is one page of the 23 Telecom messaging API docs
(SMS today; more channels planned). Base URL: https://restlink23telecom.com/api/v1,
auth via the X-API-Key header. Match errors on the error_code field, never on
description text. Full docs: https://docs.23telecom.co.uk/llms-full.txt · Schemas: https://docs.23telecom.co.uk/openapi.yaml

API keys are the primary way to authenticate. Each key is scoped by
**permissions** and bound to one **workspace**, so you can issue separate keys
per application, per environment, or per team — and revoke them independently.

## Create a key

1. Log in to the [customer portal](https://restlink23telecom.com).

2. Go to **Settings → API Keys** and click **Create API Key**.

3. Pick the permissions the key needs (see the table below) and, optionally,
   enable **HMAC signature verification** for high-security environments.

4. Copy the key (`sk_prod_…`). It is shown **only once** — store it in your
   secrets manager right away.

  Enable **Test mode** when creating a key to get a [sandbox
  key](/sandbox) (`sk_test_…`) — sends are simulated and free, delivery
  webhooks are synthesized, and nothing touches your balance.

  An API key always routes traffic to the workspace it was created in. To send
  from a different workspace, create a key in that workspace — see
  [workspaces](/account/workspaces).

## Permissions

Keys follow a deny-by-default model: a key can only call endpoints its
permissions allow.

| Permission | Grants |
| --- | --- |
| `*` | Full access |
| `sms.send` | Send SMS (`POST /sms/send`) |
| `sms.read` | Status, stats, messages, unified view (`GET /sms/*`) |
| `balance.read` | Balance (`GET /user/balance`) |
| `pricing.read` | Pricing (`GET/POST /user/pricing`) |
| `payments.read` | Payments (`GET /user/payments`) |
| `webhooks.read` | Webhook settings and logs (`GET /user/webhooks`, `GET /user/webhooks/logs`) |
| `webhooks.write` | Update webhooks and send tests (`PUT /user/webhooks`, `POST /user/webhooks/test`) |

### Recommended configurations

| Use case | Permissions |
| --- | --- |
| Send + track delivery | `sms.send`, `sms.read` |
| Send only (fire & forget) | `sms.send` |
| Dashboard / monitoring | `sms.read`, `balance.read` |
| Webhook management | `webhooks.read`, `webhooks.write` |
| Full API access | `sms.send`, `sms.read`, `balance.read`, `pricing.read`, `payments.read`, `webhooks.read`, `webhooks.write` |

Calling an endpoint outside the key's permissions returns `403 FORBIDDEN`.

## Key security best practices

- **One key per application and environment.** Separate keys for staging and
  production let you rotate or revoke one without touching the other.
- **Grant the minimum permissions.** A background sender rarely needs
  `payments.read`.
- **Store keys in environment variables** or a secrets manager. Never commit
  them to version control or embed them in client-side code.
- **Rotate on staff changes** and on any suspicion of exposure — create a new
  key, switch traffic, then delete the old one.
- **Enable HMAC signing** for environments where transport interception is a
  concern — see [authentication](/authentication#api-key-with-hmac-signature).

## Errors you may see

| HTTP | Code | Meaning |
| --- | --- | --- |
| 401 | `INVALID_API_KEY` | Key not found or malformed |
| 401 | `API_KEY_INACTIVE` | Key exists but is disabled |
| 401 | `API_KEY_EXPIRED` | Key past its expiry date |
| 403 | `FORBIDDEN` | Key lacks the permission for this endpoint |

Full list: [error reference](/reference/errors).