# Authentication & scopes

> How API keys work, the four scopes, and the rules for keeping them safe.

Source: https://www.propexecutor.com/docs/authentication
Whole reference in one file: https://www.propexecutor.com/doc.md

Every request carries an API key as a bearer token. A key belongs to your **organization**, not to the person who created it, so staff turnover never silently breaks a running integration.

<!-- Every request -->
```bash
curl https://api.propexecutor.com/v1/accounts \
  -H "Authorization: Bearer pfx_live_YOUR_KEY"
```

## Issuing a key

Keys are created in the admin panel, under [Developer](https://app.propexecutor.com/developer), by a signed-in owner or admin. The API cannot create keys — that is deliberate, so one leaked credential cannot mint its own replacement and make revoking it pointless.

> **The token is shown once**
> We store a hash, not the key. When you create one, the full token appears in that response and never again. If you lose it, revoke it and issue another — there is no way for us to recover it, including for support.

## Scopes

A key does only what you tick. A new key with no scopes authenticates successfully and can do nothing, which is the safe default and a useful state in its own right.

| Scope | Grants | Care |
| --- | --- | --- |
| `accounts:read` | List and read accounts, trades, positions, rule flags, analytics and the equity curve. | Safe. Start here. |
| `accounts:write` | Provision accounts, assign and reassign traders, archive accounts, create traders. | Provisioning **spends account credits permanently**. |
| `credentials:read` | Read and rotate the executor password for an account. | Hands out **working logins**. Anyone with that password can trade that account. |
| `trading:write` | Open and close positions on an account, and force a manual breach. | Acts as the trader. Most firms never need this. |

A call without the scope it needs returns `403` with code `missing_scope`, naming the scope it wanted. That is a 403 and not a 401 on purpose: the credential is fine, the permission is not, and a client that re-authenticates on a 401 would otherwise loop.

<!-- 403 Forbidden -->
```json
{
  "error": {
    "code": "missing_scope",
    "message": "this API key does not have the `accounts:write` scope"
  }
}
```

## Key format

| Field | Type | Description |
| --- | --- | --- |
| `pfx_live_…` | string | 49 characters: the `pfx_live_` marker plus 40 random characters. The marker is there so secret scanners and pre-commit hooks can spot one in a diff. |
| `prefix` | string | The first 17 characters, which the panel lists so you can tell two keys apart. Not a secret, and not enough to reconstruct the key. |

## Handling keys safely

- **Server-side only.** Never put a key in browser JavaScript, a mobile app or anything else a user can read. Call the API from your backend and pass the results to your own frontend.
- **One key per system.** Separate keys for your dashboard, your onboarding job and your staging environment. Rate limits are per-key, so one runaway script cannot starve the others — and you can revoke the one that leaked without taking everything down.
- **Least scope.** A dashboard that only draws charts needs `accounts:read` and nothing else.
- **Rotate by overlap.** Issue the new key, deploy it, confirm traffic on it, then revoke the old one. Revocation takes effect immediately, so revoking first means downtime.

## Revoking

`POST /api-keys/:id/revoke` · scope `panel session`

Revoke from the panel, or from your own backend with an admin session. The next request on that key returns `401` with `invalid_api_key`. Keys are revoked rather than deleted, so a key id in an old webhook delivery or a support thread still resolves.

## Authentication errors

| Status | Code | Means |
| --- | --- | --- |
| `401` | `unauthorized` | No `Authorization` header, or not a bearer token. |
| `401` | `invalid_api_key` | Unknown, malformed or revoked key. One message for all three, so a caller learns nothing about which keys exist. |
| `402` | `payment_required` | Your organization has not completed its plan purchase yet. See [limits](https://www.propexecutor.com/docs/limits). |
| `403` | `missing_scope` | Valid key, wrong scope. |
