---
title: "Authentication"
url: "https://coldmeetings.com/docs/authentication.md"
canonical_url: "https://coldmeetings.com/docs/authentication"
last_updated: "2026-10-23"
type: "conceptual"
summary: "How ColdMeetings API keys work: the cm_live_ bearer token, how keys are stored and how to get one."
related: ["https://coldmeetings.com/docs/quickstart.md", "https://coldmeetings.com/docs/rate-limits.md", "https://coldmeetings.com/docs/errors.md"]
---

# Authentication

> **For AI agents:** the complete index of ColdMeetings pages is [llms.txt](https://coldmeetings.com/llms.txt). Every page is also served as markdown at its URL plus .md.

How ColdMeetings API keys work: the cm_live_ bearer token, how keys are stored and how to get one.

Every operation under `/v1` needs an API key sent as a bearer token. Keys start with `cm_live_`. The public endpoints `/health`, `/plans`, `/openapi.json` and `/llms.txt` need no key.

> The operator creates an account and its first key on request, and you see the key once.

## Send your key

Put the key in the `Authorization` header.

**cURL**

```bash
curl YOUR_API_BASE_URL/v1/account \
  -H "Authorization: Bearer $COLDMEETINGS_API_KEY"
```

**Node.js**

```js
const res = await fetch('YOUR_API_BASE_URL/v1/account', {
  headers: { Authorization: `Bearer ${process.env.COLDMEETINGS_API_KEY}` },
});
```

**Python**

```python
import os
import requests

res = requests.get(
    "YOUR_API_BASE_URL/v1/account",
    headers={"Authorization": f"Bearer {os.environ['COLDMEETINGS_API_KEY']}"},
)
```

The API also accepts the key in an `X-API-Key` header for clients that cannot set `Authorization`. If both are present, the bearer token is used.

## What a key looks like

- The prefix `cm_live_` followed by 43 letters and digits, made from 32 random bytes.
- The first 12 characters after the prefix are the key's public id. That id appears in `GET /v1/account` and in logs. The rest is secret.

## How keys are stored

- Only the SHA 256 hash of the whole key and the public id are stored. A leaked database does not leak usable keys.
- The full key is shown once, when it is created. It cannot be recovered. If you lose it, the operator revokes it and issues a new one.
- A presented key is compared with the stored hash in constant time.
- A revoked key stops working at once. It answers the same `unauthorized` error as a key that never existed, so an error never tells a guesser which keys exist.

## Keep keys safe

- Use keys on a server. Never put one in a browser, a mobile app or a public repository.
- Keep one key per system so you can revoke one without stopping the others.
- Load the key from an environment variable or a secrets manager.

## Accounts and isolation

A key belongs to one account. It can read and change only that account. Asking for a campaign, domain or reply that belongs to another account answers `unknown_product`, the same as an id that does not exist.

## If authentication fails

A missing, malformed, revoked or unknown key answers HTTP 401 with the `unauthorized` code and a `WWW-Authenticate: Bearer` header. See [Errors](https://coldmeetings.com/docs/errors.md#unauthorized). Requests that fail authentication are not counted against the rate limit.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "The API key is missing, malformed, revoked or unknown.",
    "fix": "Send a valid key in the Authorization header as \"Bearer <key>\". If the key was revoked, create a new one and retry.",
    "docs_url": "/docs/errors#unauthorized",
    "retryable": false,
    "request_id": "req_Qx7RtVw3ZbN1pEs9fKd2Lm"
  }
}
```

## Related

- [Quickstart](https://coldmeetings.com/docs/quickstart.md)
- [Rate limits](https://coldmeetings.com/docs/rate-limits.md)
- [Errors](https://coldmeetings.com/docs/errors.md)
