> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.voodoo.center/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.voodoo.center/_mcp/server.

# Authentication

Authenticating to the Voodoo Center API is a two-credential flow:

1. A long-lived **API key** (`ak_...`) that you create and manage in the
   dashboard.
2. A short-lived **access token** (a Bearer JWT) that you obtain by exchanging
   the API key, and send on every API request.

You never send the raw API key to the API endpoints — you exchange it for a
token first, then authenticate with the token.

## 1. Create an API key

In the dashboard, open the **API** page and create a key.

* The raw key is shown **once**, at creation, in the format `ak_...`. Copy it
  immediately and store it somewhere secret — it is hashed server-side and can
  never be recovered.
* You can create, list and **revoke** keys from the same page. Revoking a key
  immediately blocks any new token exchanges with it (tokens already issued
  remain valid until they expire).

Treat an `ak_` key like a password. If it leaks, revoke it in the dashboard and
create a new one.

## 2. Exchange the key for an access token

`POST` your key to the token endpoint. The response contains an
`access_token` valid for **2 hours** (it carries `origin=api`).

```bash title="Exchange API key for token"
curl -X POST https://api.voodoo.center/api/v1/auth/token/client \
  -H "Content-Type: application/json" \
  -d '{"api_key":"ak_your_api_key_here"}'
```

```python title="exchange_key.py"
import httpx

resp = httpx.post(
    "https://api.voodoo.center/api/v1/auth/token/client",
    json={"api_key": "ak_your_api_key_here"},
)
resp.raise_for_status()
access_token = resp.json()["access_token"]
```

```json title="Response"
{
  "access_token": "eyJhbGciOiJFZERTQSIsImtpZCI6Ii4uLiJ9...",
  "token_type": "Bearer"
}
```

If the key is unknown or revoked you get `401` with `{"error": "Invalid
credentials"}`. If API-key auth is not enabled for your account's domain you get
`403`.

## 3. Call the API with the token

Send the token in the `Authorization` header as a Bearer credential on every
request:

```bash title="Authenticated request"
curl https://api.voodoo.center/api/v1/account/balance \
  -H "Authorization: Bearer <access_token>"
```

```python title="authenticated_request.py"
import httpx

resp = httpx.get(
    "https://api.voodoo.center/api/v1/account/balance",
    headers={"Authorization": f"Bearer {access_token}"},
)
resp.raise_for_status()
print(resp.json())  # {"balance": 1240.75}
```

## Token lifetime and refresh

Access tokens expire **2 hours** after they are issued. There is no refresh
token — when a token expires (or you get a `401` with
`code: "not_authenticated"`), simply exchange your API key again for a fresh
token.

A common pattern is to cache one token in memory and re-exchange when it is
close to expiry (or lazily, on the first `401`).

A single API key can be exchanged as often as you need. Keep the key secret and
the tokens short-lived — don't persist tokens longer than their 2-hour window.

## Authenticating the API Explorer

You don't have to run this two-step flow by hand to try the API. The
interactive **API Explorer** in the [API Reference](/api-reference) does the
token exchange for you:

* Enter **only your `ak_` API key** in the Explorer's authorization panel.
* When you run any endpoint, the Explorer calls
  `POST /api/v1/auth/token/client` with your key, reads back the `access_token`,
  and sends it as `Authorization: Bearer <token>` automatically.
* You never paste a JWT, and you don't authorize the token endpoint itself
  (it's public).

The Explorer runs from `docs.voodoo.center`, which is allow-listed by the API's
CORS policy, so its live requests reach the API and the token endpoint directly
from your browser.

## Transport

All access is over `https://api.voodoo.center`, which is fronted by Cloudflare.
Always use HTTPS; plain-HTTP requests are not supported.

Now that you can authenticate, place your first order.

Auth failures (`401`) and the full error envelope.