PPactDocs
Administration

API Keys

Create, list, rotate, revoke, and audit tenant-scoped API keys — including MCP-only keys — from Settings → API keys.

API keys

API keys authenticate machine-to-machine calls against the Pact REST API and the MCP transport. Every key is tenant-scoped: a key is only ever visible to — and only ever acts inside — the tenant that minted it. The management surface lives under /v1/api-keys (api/routes/api_keys.py) and backs the Settings → API keys page.

Admin only

Every route on /v1/api-keys is guarded by verify_api_key and require_module("admin"). You need an admin-module key or session to list, create, rotate, or revoke keys.

Secret format and storage

A key's secret is minted by generate_secret() (models/api_key.py). The environment field on the create request chooses the prefix:

  • environment: "live"pact_live_…
  • environment: "test"pact_test_…

The server stores only two things: the public prefix (the base plus the first 8 hex characters, shown in lists so you can tell keys apart) and the SHA-256 hash of the full secret. The plaintext secret is materialized exactly once, returned in the create/rotate response, and never persisted. If you lose it, you rotate — there is no reveal endpoint.

bash
curl -X POST https://api.pact.place/v1/api-keys \
  -H "Authorization: Bearer pact_live_…" \
  -H "Content-Type: application/json" \
  -d '{"name":"CI deploy key","environment":"live","scopes":["read:contacts"]}'
# → { "prefix": "pact_live_ab12cd34", "secret": "pact_live_…", "warning": "Save this secret …" }

Lifecycle

  1. 1

    Create

    POST /v1/api-keys mints a key with a name, optional scopes, rate_limit_per_minute (default 100), and optional expires_at. The plaintext secret is in the response body — copy it now.

  2. 2

    List / inspect

    GET /v1/api-keys and GET /v1/api-keys/{id} return metadata only (prefix, scopes, rate limit, last-used, rotation lineage). They never return the secret or hash. Pass include_revoked=true to see revoked keys.

  3. 3

    Rotate

    POST /v1/api-keys/{id}/rotate mints a replacement linked to the original via rotated_from_id and hands you a fresh secret. By default the old key keeps a 24-hour grace window (its revoked_at is set to now + 24h) so you can swap config without downtime. Pass immediate=true to hard-revoke the old key at once.

  4. 4

    Revoke

    DELETE /v1/api-keys/{id} sets revoked_at = now(). The next request authenticated by that key gets a 401.

MCP-only keys

Set mcp_only: true at creation to restrict a key to the MCP transport (/mcp/). If such a key is presented to the REST API, verify_api_key rejects it with 403 and a message pointing you at Settings → API keys — it deliberately fails loudly rather than falling through to a confusing 401. The client_label field (free text like "Claude Desktop", "Cursor") is shown in the key list so you can see which client each key was minted for.

Usage and audit

Every lifecycle action (created, used, rotated, revoked) writes an append-only row to api_key_audit. GET /v1/api-keys/{id}/usage?days=N returns a per-day request histogram sourced from api_key_audit rows where action = 'used' (default window 7 days, max 90). Key creation and revocation also emit SOC-2 audit events (auth.api_key.created / auth.api_key.deleted) and domain events (api_key.created, api_key.rotated, api_key.revoked) that outbound webhooks can subscribe to.