Authentication
Partners authenticate with an API key — a pat_-prefixed bearer token — on every request:
curl https://api.example.com/api/v1/loans \
-H "Authorization: Bearer pat_1a2b3c…"
How keys are scoped
Keys are minted under a shared, login-less service account (partner-api@service.local) that
holds the Partner role. This decouples partner keys from any staff member's account — a key
outlives personnel changes and can't be widened by changes to a human's permissions.
A key's effective permissions are the intersection of the service account's permissions and the key's own scopes. Partner keys are restricted to the partner-loan family:
partner.loans.read— read loans (GET).partner.loans.write— create and service loans (POST).
Because the scopes never include loans.*, the key authenticates fine but is forbidden on the
internal admin /api/loans (a 403, not a 401) — the contract boundary between partners and staff.
Minting a key (staff)
An administrator with partner.keys.manage mints a named key for a partner. The name identifies the
partner; the raw key is returned once and never retrievable again:
curl -X POST https://admin.example.com/api/partner-keys \
-H "Content-Type: application/json" \
--cookie "…admin session…" \
-d '{ "name": "acme-partner" }'
{ "id": 42, "name": "acme-partner", "prefix": "pat_1a2b3c4d", "key": "pat_1a2b3c4d…full…" }
You may narrow the scopes (e.g. a read-only key), but never widen them beyond partner.loans.* — the
endpoint rejects any other scope with a 400. Hand the key value to the partner over a secure
channel; store only the prefix for your own reference.
Read-only vs read-write
A key scoped to partner.loans.read can list and fetch loans but is 403 on any write
(create/activate/pay). Give a partner the narrowest key that fits their integration.