> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chargeexpressltd.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> Bearer API keys, sandbox and live environments, scopes, and how to keep keys safe.

## Sending your key

Authenticate every request with an API key in the `Authorization` header:

```bash theme={null}
curl https://partners.chargeexpressltd.com/api/public/v1/drivers \
  -H "Authorization: Bearer cxk_live_4fT9qLz2Wm8cR1vXb6Np0sYk3Hd7Ge5Ja2Uo9Ti4Qr1"
```

Only `GET /openapi.json` works without a key. There is no query-string or basic-auth alternative.

A key belongs to exactly one fleet and acts with a fleet manager's access to that fleet only. There is no fleet id in any path: the key decides the fleet.

## Key format

```text theme={null}
cxk_<environment>_<43 characters of [0-9A-Za-z]>
```

The environment is part of the key. You choose it for each key when you create it in the portal (**Developers → API keys → Create key → Live** or **Sandbox**):

| Environment | Prefix | Acts on | Events |
| - | - | - | - |
| Live | `cxk_live_` | Your real fleet: real drivers, the ChargeExpress charger network, ExpressPay payments and real invoices. | `livemode: true` |
| Sandbox | `cxk_test_` | Your fleet's [sandbox](/guides/sandbox): a separate copy, created on first use, with fake drivers, simulated chargers and simulated payments. Nothing is billed. | `livemode: false` |

Each environment has its own base URL: live keys work only on `https://partners.chargeexpressltd.com/api/public/v1`, sandbox keys only on `https://stage.partners.chargeexpressltd.com/api/public/v1`. A key sent to the other one is rejected with `wrong_environment`.

**Live keys come from the production fleet portal.** Other portals only offer sandbox keys, and a `cxk_live_` key is rejected with `invalid_api_key` anywhere live mode isn't available.

The two environments are fully separate. Drivers, vehicles, sessions, the wallet, webhook endpoints and events in one are never visible from the other, and the portal's **Developers** pages show one environment at a time. Start with a sandbox key and switch to a live key when you go live; see the [Quickstart](/quickstart#going-live).

The portal shows the first characters of each key (its prefix, for example `cxk_live_4fT9qLz2Wm8c`) so you can tell keys apart. ChargeExpress stores only a SHA-256 hash of the key, so nobody, including ChargeExpress, can show you the full key again.

## Authentication failures

| Status | `code` | When |
| - | - | - |
| 401 | `missing_api_key` | No `Authorization: Bearer …` header. |
| 401 | `invalid_api_key` | The key is malformed, unknown, revoked or expired; it is a live key where live mode isn't available; or its fleet is deactivated or not approved. |
| 401 | `wrong_environment` | A live key sent to the stage URL, or a sandbox key sent to the production URL. The message names the right URL. |
| 403 | `missing_scope` | The key is valid but lacks the scope the endpoint needs. |

All the `invalid_api_key` cases deliberately look identical, so the response doesn't reveal which one applied. If a fleet is deactivated, every key it has stops working straight away. Revocation is immediate too: there is no key cache.

## Scopes

Each key carries one or more scopes, chosen when it is created. The portal offers **Read only** (`fleet:read`) and **Full access** (every scope) presets, or you can pick scopes yourself.

| Scope | Grants |
| - | - |
| `fleet:read` | Read the fleet, drivers, vehicles, limits, charge points, sessions, wallet, fundings, invoices, webhook endpoints and events. Required by every `GET`. |
| `drivers:write` | Create, update, suspend and remove drivers; upload driver photos. |
| `vehicles:write` | Create, update and retire vehicles; upload vehicle images. |
| `limits:write` | Replace fleet limits and per-driver limit overrides. |
| `wallet:write` | Start wallet top-ups, pay usage invoices, and set the low-balance alert (`PATCH /fleet`). In the sandbox, also settle top-ups with `/sandbox/fundings/{fundingId}/succeed` and `/fail`. |
| `webhooks:write` | Create, update and delete webhook endpoints, rotate secrets, send test events and redeliver events. |

> [!NOTE]
>
> Write scopes do **not** include `fleet:read`. A key that creates drivers and then reads them back needs both `drivers:write` and `fleet:read`.

Each endpoint's required scope is listed in the [API reference](/api-reference/introduction).

## Key hygiene

* **One key per system.** Give your accounting sync, your dispatch tool and your BI export separate keys with only the scopes each needs. Revoking one then doesn't break the others.
* **Server side only.** Keep keys in a secret manager or environment variables on your servers. Never ship them in mobile apps, browsers, logs or source control.
* **Set an expiry** for keys used by contractors or temporary jobs.
* **Watch "last used".** The portal shows when each key was last used. A key that hasn't been used in months is a candidate for revocation.
* **Revoke on suspicion.** Revoking takes effect on the next request.

A fleet can have at most 10 active (non-revoked, unexpired) keys in each environment: 10 live and 10 sandbox. API keys cannot create or revoke API keys; that's done in the portal.

## Rotating a key

Keys don't rotate in place. To rotate without downtime:

**1. Create a new key**

Create a key with the same environment and scopes in **Developers → API keys**.

**2. Deploy it**

Update your secret store and roll out the new key to every instance of your system.

**3. Confirm the old key is idle**

Wait until the old key's **Last used** time stops moving.

**4. Revoke the old key**

Revoke it in the portal. Any request still using it now gets `invalid_api_key`.

Webhook signing secrets rotate differently; see [Rotating the signing secret](/webhooks/overview#rotating-the-signing-secret).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.