API key management
API keys are the credential /api/v1 checks on every request. This page covers how to mint and revoke them, the on-disk format, and how lastUsedAt works.
UI flow
The key-management UI lives in the dashboard at Settings → API keys. It ships with FUL-13.
To mint a key:
- Open Settings → API keys → New key.
- Enter a human-readable name (e.g.
ci-bot,dashboard-readonly). The name is shown in the table and in audit logs; it has no effect on authentication. - Choose one or more scopes. See authentication — scopes.
- Click Create. The plaintext key is displayed once. Copy it immediately and store it in a secret manager.
- The dashboard never shows the plaintext again. If you lose it, revoke and mint a new one.
The keys table on the same page lists each key’s name, scopes, createdAt, lastUsedAt, and revokedAt, along with a Revoke action.
Key shape
A plaintext key looks like:
ts_a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90ts_prefix marks it as a Trademark Sentinel key. Secret-scanning tools and the in-app paste detector use this prefix to flag accidental commits.- 32 cryptographically random bytes encoded as 64 hex characters follow the prefix (256 bits of entropy).
- The full key is 67 characters long (
ts_+ 64 hex chars).
Server-side storage
The plaintext is never persisted. On creation the server:
- Generates 32 cryptographically random bytes.
- Renders them as
ts_<hex>and returns the plaintext in the create response. - Computes SHA-256 over the plaintext and stores only that digest in
ApiKey.hash(Prisma schema, plan §3 / §11).
On every request the server SHA-256-hashes the incoming Bearer token and looks the row up by hash. The ApiKey.hash column has a unique index, so the lookup is a single equality probe.
ApiKey columns visible to clients (over the API and in the dashboard table):
| Field | Type | Notes |
|---|---|---|
id | string | Use this to revoke the key. |
name | string | User-chosen label. |
scopes | string[] | See authentication — scopes. |
lastUsedAt | string | null | ISO-8601; updated on use (see below). |
createdAt | string | ISO-8601. |
revokedAt | string | null | ISO-8601 once revoked; otherwise null. |
hash is never returned over the API.
Revocation
DELETE /api/v1/api-keys/{id}Authenticated by the dashboard session cookie (you cannot revoke a key with itself). Sets revokedAt to the current server time and from that moment forward all requests bearing that key fail with 401 unauthenticated.
Revocation is permanent and irreversible. To “un-revoke” a key, mint a new one — they are cheap, and rotating is the supported way to replace one.
Example
curl -sS -X DELETE \ https://api.trademarksentinel.app/api/v1/api-keys/ckxyz0000000abcdef \ -H "Cookie: session=<dashboard-session-cookie>"{ "data": { "id": "ckxyz0000000abcdef", "name": "ci-bot", "scopes": ["watches:write"], "lastUsedAt": "2026-05-01T22:14:00.000Z", "createdAt": "2026-04-12T08:00:00.000Z", "revokedAt": "2026-05-02T11:32:08.123Z" }}A repeat DELETE on an already-revoked key is a no-op and returns 200 with the same body. Revoking a key that does not exist (or that belongs to another user) returns 404 not_found.
lastUsedAt semantics
lastUsedAt is the timestamp of the most recent successful authentication using that key. It is intended for the dashboard’s “is this key still in use” view and for audit purposes.
- It is updated on requests that pass authentication, even if the request later fails (e.g. validation, rate limit, scope mismatch). Authentication success is the trigger.
- Updates are best-effort and coalesced: the server may batch writes per key over short windows (seconds, not minutes) to avoid hammering the row on every request. So the value is approximately current, not exact-to-the-millisecond.
- A revoked key never updates
lastUsedAt(the request fails authentication). - Newly minted, unused keys have
lastUsedAt: null.
If you need stronger audit guarantees than lastUsedAt (e.g. per-request access logs), the dashboard’s Settings → API keys → Audit log view is the supported surface — it ships alongside the management UI in FUL-13.