> ## 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.

# Quickstart

> Create an API key, make your first request, register a webhook endpoint and verify its signature.

This takes about ten minutes. You need to be a manager of an approved, active fleet in the ChargeExpress fleet portal.

Start with a **sandbox key**. The [sandbox](/guides/sandbox) is a private copy of your fleet with fake drivers, simulated chargers and simulated payments, so nothing you do while building touches real people, chargers or money. When you're ready, [go live](#going-live) with a live key.

## 1. Create a sandbox API key

**1. Open Developers → API keys**

Sign in to the fleet portal, open **Developers** in the sidebar, then the **API keys** tab.

**2. Create a key**

Click **Create key** and choose **Sandbox**. Give it a name that says which system will use it (for example `accounting-sync-dev`), and pick its scopes. **Read only** grants `fleet:read`; **Full access** grants every scope. You can also set an expiry date.

**3. Copy the key now**

The full key, such as `cxk_test_7Hq2…`, is shown **once**. Store it in your secret manager. ChargeExpress keeps only a hash, so a lost key cannot be recovered; revoke it and create another.

> [!WARNING]
>
> API keys act with a fleet manager's access to your fleet. Keep them on your servers. Never put a key in a mobile app, a browser, or a repository.

## 2. Make your first request

Retrieve your fleet. The key needs the `fleet:read` scope. With a sandbox key this returns your fleet's sandbox copy, which is created on your first request.

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

```javascript Node.js theme={null}
// Node 18+ (global fetch)
const res = await fetch('https://stage.partners.chargeexpressltd.com/api/public/v1/fleet', {
  headers: { Authorization: `Bearer ${process.env.CHARGEEXPRESS_API_KEY}` },
});
const body = await res.json();
if (!res.ok) {
  throw new Error(`${body.error.code}: ${body.error.message} (${body.error.request_id})`);
}
console.log(body.name, body.status);
```

```python Python theme={null}
import os
import requests

res = requests.get(
    "https://stage.partners.chargeexpressltd.com/api/public/v1/fleet",
    headers={"Authorization": f"Bearer {os.environ['CHARGEEXPRESS_API_KEY']}"},
    timeout=30,
)
body = res.json()
if not res.ok:
    raise RuntimeError(f"{body['error']['code']}: {body['error']['message']} ({body['error']['request_id']})")
print(body["name"], body["status"])
```

A successful response looks like this:

```json Response theme={null}
{
  "object": "fleet",
  "id": "s5k9m3p7r1t5v9x3z7b1d5f9",
  "name": "Accra Metro Logistics (sandbox q8h2kz4m)",
  "description": "Last-mile delivery vans",
  "status": "active",
  "contact_phone": "+233201234567",
  "unit_price": "3.85",
  "currency": "GHS",
  "low_balance_threshold_kwh": "500.0000",
  "created_at": "2026-03-02T08:15:00.000Z"
}
```

Every response, successful or not, has an `X-Request-Id` header such as `req_m2k8q4w9z1x7c3v5b6n0p2r4`. Log it; it's the fastest way for support to find your request. If something goes wrong you'll get an [error object](/errors) instead.

## 3. Register a webhook endpoint

Webhooks tell you when sessions finish, the wallet is funded, and more. Your endpoint must be a public `https://` URL on port 443 or 8443.

You can register it in the portal (**Developers → Webhooks**) or with the API, using a key with `webhooks:write`:

```bash curl theme={null}
curl https://stage.partners.chargeexpressltd.com/api/public/v1/webhook_endpoints \
  -H "Authorization: Bearer $CHARGEEXPRESS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6c0f3e9a-endpoint-create" \
  -d '{
    "url": "https://hooks.example.com/chargeexpress",
    "description": "Sandbox listener",
    "events": ["session.completed", "wallet.funded", "wallet.low_balance"]
  }'
```

The response includes the signing `secret`. Like the API key, it is returned only here (and from [rotate\_secret](/webhooks/overview#rotating-the-signing-secret)):

```json Response theme={null}
{
  "object": "webhook_endpoint",
  "id": "we_m4n8p2q6r0s4t8u2v6w0x4y8",
  "url": "https://hooks.example.com/chargeexpress",
  "description": "Sandbox listener",
  "events": ["session.completed", "wallet.funded", "wallet.low_balance"],
  "status": "enabled",
  "disabled_reason": null,
  "failing_since": null,
  "created_at": "2026-10-13T09:00:00.000Z",
  "updated_at": "2026-10-13T09:00:00.000Z",
  "secret": "whsec_x2bq8Vt0rZpK4m9LwY7cN1sD5fH3gJ6aE8uR0iO2kTe"
}
```

Use `["*"]` to subscribe to every event type, including ones added later.

An endpoint registered with a sandbox key belongs to the sandbox: it receives only sandbox events (`livemode: false`).

## 4. Verify the signature

Every delivery is a `POST` with a JSON [event](/webhooks/events) and an `X-ChargeExpress-Signature` header. Verify it against the **raw** request body before you trust the payload. This Express handler uses the verifier from [Verifying signatures](/webhooks/verifying-signatures):

```javascript server.js theme={null}
const express = require('express');
const { verifyChargeExpressSignature } = require('./verify');

const app = express();

app.post(
  '/chargeexpress',
  express.raw({ type: 'application/json' }), // keep the exact bytes
  (req, res) => {
    const ok = verifyChargeExpressSignature(
      req.body,
      req.get('X-ChargeExpress-Signature'),
      process.env.CHARGEEXPRESS_WEBHOOK_SECRET,
    );
    if (!ok) return res.status(400).send('Bad signature');

    const event = JSON.parse(req.body.toString('utf8'));
    // Respond fast, then process. Dedupe on event.id.
    res.sendStatus(200);
    queueForProcessing(event);
  },
);

app.listen(3000);
```

## 5. Send a test event

Click **Send test** on the endpoint in the portal, or call the API:

```bash curl theme={null}
curl -X POST https://stage.partners.chargeexpressltd.com/api/public/v1/webhook_endpoints/we_m4n8p2q6r0s4t8u2v6w0x4y8/test \
  -H "Authorization: Bearer $CHARGEEXPRESS_API_KEY"
```

ChargeExpress sends a signed `ping` event to the endpoint straight away and tells you what your server answered. To try a real event shape, send `{"type": "session.completed"}` (or any other [event type](/webhooks/events)) as the body:

```json Response theme={null}
{
  "object": "webhook_test",
  "event_id": "evt_p0q1r2s3t4u5v6w7x8y9z0a1",
  "type": "ping",
  "succeeded": true,
  "response_status": 200,
  "response_body": "OK",
  "error": null,
  "duration_ms": 184
}
```

Test events are never stored, so they don't appear in `/events` and are never retried.

## 6. Run a session in the sandbox

Start a session on a simulated charger from **Developers → Sandbox** in the fleet portal, and watch `session.started`, `session.stopped_by_limit` and `session.completed` arrive at your endpoint within seconds. The [Sandbox guide](/guides/sandbox#walkthrough) walks through it, including a simulated top-up.

## Going live

When your integration works against the sandbox:

1. Create a **live** key (`cxk_live_…`) in the **production** fleet portal under **Developers → API keys → Create key → Live**. Live keys are only offered there.
2. Swap it into your secret store in place of the sandbox key, and change the base URL to `https://partners.chargeexpressltd.com/api/public/v1`. Live keys only work there, and sandbox keys only work on `https://stage.partners.chargeexpressltd.com/api/public/v1`.
3. Register your webhook endpoints again with the live key, and store their new signing secrets. Sandbox endpoints never receive live events.
4. If one server receives both sandbox and live events, branch on the event's `livemode`.

From then on your calls act on your real fleet: drivers get login invites, you see their real charging sessions, and top-ups go through ExpressPay.

## Next steps

* [Sandbox](/guides/sandbox): simulated chargers, payments and resets.
* [Authentication](/authentication): scopes, sandbox vs live keys, rotation.
* [Charging flow](/guides/charging-flow): follow your drivers' sessions and read their cost.
* [Webhooks overview](/webhooks/overview): retries, ordering and redelivery.


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