Skip to content

Authentication and scopes

Every request to the External API is authenticated with an API key sent in the X-API-Key header.

Terminal window
curl -sS https://api.claytonpower.com/api/v1/external/systems \
-H "X-API-Key: cp_live_Ab3xK9pQ2vNmR7sT4uW1yZ0cD6eF8gH5jL3nP7qS9tU"

There is no other scheme. An Authorization: Bearer token from one of the Clayton Power apps is not accepted on /api/v1/external/* and is answered with 401.

cp_live_Ab3xK9pQ2vNmR7sT4uW1yZ0cD6eF8gH5jL3nP7qS9tU
└──┬───┘└─────────────────────┬────────────────────┘
prefix 43 characters, base64url

A key is the literal prefix cp_live_ followed by 43 base64url characters encoding 32 cryptographically random bytes. Treat the whole string as opaque and compare it byte for byte; do not trim, lowercase or re-encode it.

The first 12 characters (for example cp_live_Ab3x) are stored and shown in the key list so you can tell your keys apart. The rest is stored only as a SHA-256 hash.

A key is displayed exactly once, at creation. There is no endpoint and no support procedure that can reveal it again.

A key carries one or more scopes, chosen when it is created. A scope is a permission to call a group of endpoints; a key without the right scope gets 403, even for its own company’s data.

Scope Grants
systems:read List systems and read a single system
telemetry:read Latest snapshot, energy series, state-of-charge series, health
alerts:read List and read alerts, read a system’s alert settings
alerts:manage Change a system’s alert settings
webhooks:manage Create, read, update and delete webhook subscriptions, rotate a secret, send a test event, read deliveries

Per endpoint:

Endpoint Scope
GET /systems systems:read
GET /systems/{serialNumber} systems:read
GET /systems/{serialNumber}/telemetry/latest telemetry:read
GET /systems/{serialNumber}/telemetry/energy telemetry:read
GET /systems/{serialNumber}/telemetry/soc telemetry:read
GET /systems/{serialNumber}/health telemetry:read
GET /alerts, GET /alerts/{id} alerts:read
GET /systems/{serialNumber}/alert-settings alerts:read
PUT /systems/{serialNumber}/alert-settings alerts:manage
`GET POST

Grant the narrowest set that does the job. A read-only reporting integration wants systems:read and telemetry:read and nothing else.

Scopes are fixed for the life of a key. To change them, create a new key with the scopes you want and revoke the old one.

A key belongs to one company and sees only that company’s systems. There is no cross-company or distributor key in v1.

A serial number that belongs to another company is answered with 404, exactly as an invented serial number is. This is deliberate: a 403 would confirm that the serial exists, which would let anyone with a key enumerate the fleet.

Expiry is optional and set at creation. After the expiry moment the key is rejected with 401.

Revocation is immediate. Revoke a key in fleet-manager under Settings -> API keys. Revoking an already revoked key is not an error. There is no undo - a revoked key can never be reactivated.

Rotation is create-then-revoke; there is no in-place rotation of an API key in v1. The order that avoids downtime:

  1. Create a new key with the same scopes.
  2. Deploy it to your integration.
  3. Confirm the integration is using it.
  4. Revoke the old key.

A company can hold at most 20 active keys. Revoked keys do not count towards the limit. If creation fails because the limit is reached, revoke keys you no longer use.

Each key records when it was last used, refreshed at most once every five minutes, so the timestamp tells you whether a key is still in service but is not an audit trail of individual calls.

401 Unauthorized - the request was not authenticated:

  • no X-API-Key header, or an empty one
  • a key that does not exist, has been revoked, or has expired
  • a session token from the Clayton Power apps instead of an API key

Retrying does not help. Check the key.

403 Forbidden - the key is valid but does not carry the scope the endpoint requires. Create a key with the missing scope; the same request will keep failing until you do.

Neither response reveals whether the resource in the path exists.

Both use the standard error shape described in Errors and rate limits.

Limits are applied per key, not per company, so one busy integration cannot starve another. The current limit is 600 requests per 60 seconds. See Rate limits.