# Authentication and API keys

> Create the right API key, send it with every request, and keep it safe.

Source: https://docs.vlayers.ai/get-started/authentication

Kind: How-to guide

## What you'll learn

How a request proves it comes from your workspace, which key to create for which job, the difference between test and live keys, and how to limit and rotate a key.

## Prerequisites

- A VoiceLayer account with access to **Developers → API keys** in the dashboard.

## Step 1: Create a secret key

Your servers use a **secret key**. It can do everything the API offers for your workspace.

1. In the dashboard, open **Developers → API keys** and choose **Create key**.
2. For "What is this key for?", pick **Your servers or the SDK**.
3. Choose an **Environment**: **Testing** for development, **Live** for real customers.
4. Optionally set **Limit to channels** (for example `sms, telegram`) so the key can only act on those channels.
5. Copy the key now. The dashboard shows it once.

Other kinds of key exist for other jobs: an MCP key for Claude Code, Cursor or another [MCP](https://docs.vlayers.ai/developers/mcp) client, a publishable key for the chat widget on your website, and worker keys for [voice agents you host](https://docs.vlayers.ai/developers/sdks/voice-agents). The API routes on this site need a secret key; a publishable key is refused.

## Step 2: Send the key with every request

Put the key in the `Authorization` header as a bearer token:

```bash
curl https://api.vlayers.ai/v1/channels \
  -H "Authorization: Bearer $VOICELAYER_API_KEY"
```

The SDKs take the key once:

cURL:

```bash
curl https://api.vlayers.ai/v1/channels \
  -H "Authorization: Bearer $VOICELAYER_API_KEY"
```

TypeScript:

```ts
import { createVoiceLayerClient, listChannels } from '@voicelayer/api';

const client = createVoiceLayerClient({ apiKey: process.env.VOICELAYER_API_KEY!, throwOnError: true });

const { data } = await listChannels({
  client
});
console.log(data.channels);
```

Python:

```python
import os
import voicelayer

config = voicelayer.Configuration(access_token=os.environ["VOICELAYER_API_KEY"])
with voicelayer.ApiClient(config) as client:
    api = voicelayer.ChannelsApi(client)
    result = api.list_channels()
    print(result.channels)
```

A request without a valid key is refused with `401 unauthorized`.

## Step 3: Choose test or live

| | Test key | Live key |
|---|---|---|
| Prefix | `vl_test_` | `vl_live_` |
| Reaches a real carrier | No: a simulated carrier | Yes |
| Costs | Nothing (daily caps apply) | Normal prices |
| Who it can message | Only [reserved test addresses](https://docs.vlayers.ai/developers/test-mode#reserved-test-addresses) | Real addresses only |

Your code is the same for both. Only the key, and the addresses you message, change. See [Test mode](https://docs.vlayers.ai/developers/test-mode).

## Scopes

Each API operation needs one scope. A secret key from the dashboard holds all of them; the scope tells you what a request needs and what a missing scope means in an error.

| Scope | Lets the key |
|---|---|
| `messages:send` | Send messages and reply in conversations; simulate inbound messages in test mode |
| `messages:read` | List messages and conversations, and read a conversation |
| `conversations:control` | Hand a conversation to the agent, your code or a person |
| `events:read` | Read events from the stream and the pull endpoint |
| `channels:read` | List your channels and WhatsApp templates |
| `webhooks:manage` | Create, change and delete webhook subscriptions, and read and retry deliveries |
| `logs:read` | Read your API request logs |

A key without the scope an operation needs is refused with `403 insufficient_scope`; `details.required` names the missing scope. A key limited to some channels gets the same error, or `404 not_found`, for anything outside them.

## Keep keys secret

- Keep secret keys on your servers. Never put one in a browser, a mobile app or a public repository.
- Read the key from an environment variable or a secrets manager, as the examples do with `VOICELAYER_API_KEY`.
- Use a separate key per service and environment, so you can revoke one without touching the others.

## Rotate a key

1. Create a new key in **Developers → API keys**.
2. Deploy it to your service.
3. Delete the old key. Requests that still use it start failing with `401 unauthorized`.

## Test it

Call `GET /v1/channels` with your test key (Step 2). A `200` with your channels means the key works.

## Common errors

| Error | Status | How to fix |
|---|---|---|
| [`unauthorized`](https://docs.vlayers.ai/errors/unauthorized) | 401 | Send `Authorization: Bearer <key>` with a secret key from the dashboard (Developers → API keys). Test keys start with `vl_test_`. |
| [`insufficient_scope`](https://docs.vlayers.ai/errors/insufficient_scope) | 403 | Use a key with the scope named in `details.required`, or one whose restriction covers this channel or Binding. |
| [`no_project`](https://docs.vlayers.ai/errors/no_project) | 403 | Use an API key created inside the workspace you mean to act on. |
| [`test_address_required`](https://docs.vlayers.ai/errors/test_address_required) | 422 | Use a reserved test address, or a live key to reach a real person. |
| [`test_address_not_allowed`](https://docs.vlayers.ai/errors/test_address_not_allowed) | 422 | Use a test key (`vl_test_…`) for test addresses. |

## Next steps

- [Quickstart](https://docs.vlayers.ai/get-started/quickstart): receive and answer a message.
- [Core concepts](https://docs.vlayers.ai/get-started/concepts): workspaces, senders and conversations.
- [Rate limits](https://docs.vlayers.ai/developers/rate-limits): how many requests a key may make.
