API keys
The /keys page lists every key minted under your tenant — across both modes (live / test) and both types (secret sk_* / public pk_*) — and lets authorized roles mint or revoke them.
Key types
The environment and the type are both encoded in the key string. There are no separate sandbox URLs — api.drukverify.com accepts both modes; the gateway routes based on the prefix.
| Prefix | Type | Where it lives | Used for |
|---|---|---|---|
dv_sk_live_… | secret | Server-side only — never ship to a mobile app | Minting session tokens, calling admin endpoints, server-side OCR / face / liveness calls |
dv_sk_test_… | secret | Server-side, dev environments | Same as above but routes to test-mode data — no PII retention, separate quotas |
dv_pk_live_… | public | Embeddable in your mobile app binary | The apiKey field on EkycConfig in the Flutter SDK; identifies the tenant + mode without granting privileged access |
dv_pk_test_… | public | Embeddable, dev environments | Same as pk_live but routes to test mode |
The pk_ keys are deliberately safe to ship in app stores. Anything they could do, an unauthenticated user could do anyway — they identify the tenant for routing and rate-limiting, not for authentication.
Role gates
The mint and revoke buttons are visible only to roles that can use them. The same gates are enforced at the API layer (POST /v1/keys, DELETE /v1/keys/:id), so a manually-crafted request from a non-permitted role still gets a 403.
| Action | admin | developer | viewer |
|---|---|---|---|
| List keys | ✅ | ✅ | ✅ |
Create dv_*_live_* | ✅ | ❌ | ❌ |
Create dv_*_test_* | ✅ | ✅ | ❌ |
| Revoke any key | ✅ | ✅ | ❌ |
Creating a key
Click Create key and fill in:
- Type —
skorpk. - Mode —
liveortest. - Name — free-form, optional. Names appear in the audit log and on the keys list — helpful when you have keys for multiple environments or services. Example: "demo-backend (staging)" or "iOS app store build".
After you submit, the full key value is displayed exactly once in a confirmation modal. Copy it into your secrets manager / .env before dismissing — there is no way to recover the plaintext later. The list view only ever shows the prefix (dv_sk_live_aBc…).
Revoking a key
Click the Revoke action on a row. Revocation is immediate — any request using the key from the next round-trip onward gets a 401 with error.type = "revoked_key". The row stays in the list (greyed out, "Revoked" badge) for audit; it's never deleted.
There's no un-revoke. Mint a new key if you change your mind.
Last-used tracking
Each row shows Last used — the timestamp of the most recent successful authentication with that key. Useful for two things:
- Identifying dead keys ("haven't been touched in 90 days — probably safe to revoke") for credential hygiene.
- Spotting suddenly-quiet keys ("our backend's
sk_livehasn't been used in 6 hours and we're not in a maintenance window — what's wrong?").
The field is updated every time a request authenticates with the key, with a small delay to avoid hammering the database on hot paths.
Audit trail
Every key creation and revocation produces an audit event visible on the audit log. The event records who did it (dashboard user id + email), when, and from which masked IP. Useful for after-the-fact "who minted that key in the middle of the night?" questions.