Issue an API Credential¶
What this is: minting (or revoking) a machine ingest credential — the bearer token
an external producer uses to push force-tracking data into the deployment over the HTTP
/api/feed/* API.
When you'd do it: wiring up NiFi or an external force-tracking source that POSTs
detections or targets, or cutting one off.
How long it takes: about two minutes to mint; revoking is instant.
Who can do this: an Admin. The API-credentials screens are session- and admin-guarded — they only show up if your own login has the Admin role. A normal Operator login won't see them.
This is a machine-to-machine credential — a separate trust root from operator, device, and server identity. It is an HS256 JWT verified against a database registry, not a Directory-signed identity, and it carries no clearance. For the full wire contract see Machine Ingest API. You'll do everything on the admin website — no command line for minting or revoking.
The shape of it¶
flowchart LR
A["Open the<br/>API Credentials page"] --> B["Choose scopes<br/>and operations"]
B --> C["Create — copy the<br/>token (shown once)"]
C --> D["Hand it to the<br/>producer (NiFi / source)"]
You pick what the token may do, the page hands you the token once, and you pass it securely to whoever is configuring the producer.
Before you start¶
- You're signed in to the admin site as an Admin.
- The signing secret is set.
WAYPOINT_API_JWT_SECRETmust be set on the web tier — the app fails closed at boot without it, and tokens can't be verified without it. See the env table in Stand up the web client. - You know what the producer needs to do — which scopes
(
detections:write/detections:read/targets:write/targets:read) and which operations (specific operation ids, or account-wide*). - A secure way to hand the token to whoever is configuring the producer.
Mint a credential¶
-
Open the API Credentials page. Navigate to
/admin/api-credentials. You'll see the list of live credentials and a control to create a new one. -
Choose the scopes. Tick the capability set the producer needs —
detections:writeto push detection rows,targets:writeto push target rows, and the…:readscopes for read access. A token is rejected with 403 at any endpoint whose scope it lacks, so grant only what the producer uses. -
Choose the operations (
ops). Either name specific operation ids, or set it account-wide with*. This is an independent axis from scopes and is checked per row against theoperation_idthe row carries — an empty ops list is deny-all. -
Set an expiry (optional). Add a hard expiry if the credential should age out on its own; leave it blank for no expiry.
-
Create — and copy the token now. On create, the token is shown once via a flash message. Copy it immediately — it is never re-retrievable; the system stores only metadata (the
kid, scopes, ops), never the secret. If you navigate away without copying it, you must revoke it and mint a fresh one. -
Hand it off. Pass the token securely to whoever is configuring the producer (NiFi, an external force-tracking source). They set it as the
Authorization: Bearer <token>on their/api/feed/*requests.
Revoke a credential¶
- On the API Credentials page, find the credential and delete it
(
DELETE /admin/api-credentials/:id). - Revocation is effective from the next request: the JWT signature still verifies (the
signing secret is unchanged), but the live lookup of its
kidin the registry now fails, so the credential is dead. There is no un-revoke — to restore access, mint a fresh credential.
Rotating the signing secret is the blunt instrument. Rotating
WAYPOINT_API_JWT_SECRETinvalidates all outstanding ingest tokens at once, with no overlap window — distinct from Directory signing-key rotation, which keeps the previous key served until its tokens expire (see Rotate keys). Use per-credential revoke for a single producer; rotate the secret only when you mean to cut everything off.
How to know it worked¶
- Mint: a
POST /api/feed/detections(or/api/feed/targets) with the bearer token returns{ accepted, rejected }. - Scope check: a token lacking the endpoint's scope returns 403.
- Revoke / expiry: a revoked or expired token returns 401 on the next request.
If something goes wrong¶
- You navigated away before copying the token. It's gone — it was shown once only. Revoke the credential on the API Credentials page and mint a fresh one.
- The producer gets 401 on every request. Either the token is revoked/expired, or the
web tier has no (or a different)
WAYPOINT_API_JWT_SECRETthan the one the token was signed with — check the env on the web tier. - The producer gets 403. The token's scopes don't include the endpoint's required scope. Mint a new credential with the right scope; you cannot widen an existing token's scopes.
- Rows are rejected with a 404 / unknown operation. The token's
opsallowlist doesn't cover theoperation_idthe rows carry, or that operation id doesn't exist.
See also¶
- Operator training index
- Machine Ingest API — the
/api/feed/*contract, scopes, batch semantics, and revocation registry this credential authenticates against - Provision feed ingestion — NiFi writing
track_hits(the other ingest path; that one uses a DB role, not a JWT) - Stand up the web client — where
WAYPOINT_API_JWT_SECRETis set - Rotate keys — Directory key rotation, contrasted with the no-overlap secret rotation above
Verified against web@feature/roadmap — admin UI /admin/api-credentials (session + admin
guarded); credential registry, findByKidLive, and revoke in web/app/domains/api_credentials/;
bearer verify and scope enforcement in web/app/middleware/api_auth_middleware.ts; per-row ops
check in web/app/domains/api/. The token is shown once on create and never stored at rest.