# Authentication and keys

Key format, the Xero-Tenant-Id rule, rotation with a grace window, and instant revocation.

## Key format

```text
xpk_live_<keyid>_<secret>
```

- `<keyid>` is public and is what we look up on. `<secret>` is 256 bits of machine entropy, stored only as a hash.
- The full credential is shown exactly once, when the key is created or rotated. It cannot be retrieved afterwards.
- `xpk_test_` keys are a separate environment: a test prefix never resolves a live key, even if the rest matches.

## The Xero-Tenant-Id rule

1. Key scoped to one organisation, header absent: we fill it in for you.
2. Key scoped to several organisations, header absent: `400 tenant_required`, listing the tenant IDs you may use.
3. Header present but not in the key's set: `403 tenant_not_allowed`, whether that organisation belongs to another account, was guessed, or does not exist.

## Rotation and revocation

- Rotation mints a replacement carrying the same organisations, scopes and allowlist, and gives the old credential a grace window (24 hours by default) during which both work.
- Revocation is immediate: keys are read from the database on every request, so there is no cache window to wait out.
- An expiry date on a key is enforced on the request path, not by a sweep, so a key stops working the moment it passes.

## Management-plane scopes

A key can additionally carry management scopes, which grant access to this platform's own control plane through the management MCP server. They are held separately from Xero scopes and have no effect on proxied traffic.

| Scope | What it grants |
| --- | --- |
| management.connections.read | Read Xero connection health and the organisations behind it |
| management.keys.read | List API keys and their scoping (never their secrets) |
| management.keys.write | Create, rotate and revoke API keys |
| management.usage.read | Read request volume, error rates and the recent request log |
| management.diagnostics.read | Inspect one request by its id, including any bodies captured during a debug window |
| management.diagnostics.write | Start and stop time-boxed body capture on a key |
| management.alerts.read | Read open and acknowledged alerts |
| management.alerts.write | Acknowledge and resolve alerts |
| management.cache.read | Read the caching settings and what caching is saving |
| management.cache.write | Purge cached responses |
| management.limits.read | Read the quota policy, per-key limits and the organisation's remaining Xero budget |
| management.limits.write | Change per-key rate limits, daily quotas and priority |
| management.idempotency.read | Read the write-safety settings and which writes are currently protected against replay |
| management.idempotency.write | Forget stored write responses, so the next retry of each reaches Xero |
| management.deferred.read | Read the deferred-execution settings and the queue of work accepted for later |
| management.webhooks.read | Read event subscriptions and their delivery history |
| management.webhooks.write | Pause, resume and re-queue event deliveries |
| management.callbacks.read | Read completion-callback endpoints and whether they are being delivered to |
| management.callbacks.write | Re-queue a completion callback that exhausted its retries |
