Authentication
Bearer tokens with Stripe-style prefixes; SHA-256 hashed at rest.
Key format
Each issued key looks like this:
qgre_live_a1b2c3d4e5f6f0e1d2c3b4a5a1b2c3d4e5f6f0e1d2c3b4a5a1b2c3d4e5f6f0e1qgre_— type prefix (always)live_ortest_— environment.test_keys are ALWAYS free + sandboxed; they never bill credits or hit the demo daily capa1b2c3d4e5f6— first 12 hex chars are the public prefix; safe to log / displayf0e1...0e1— remaining 52 hex chars are the secret half; SHA-256 hashed at rest, never retrievable after creation
Sending the key
Include the full key in the Authorization header on every request:
Authorization: Bearer qgre_live_<...>Rotation
Issue a new key, update your apps, then revoke the old one. We track the relationship server-side via the rotates_from_id column so the dashboard can hint at active overlap windows. The audit log keeps revoked keys listed forever — a leaked key with last_used_at > revoked_at is investigated.
Constant-time comparison
Server-side, prefix lookup hits an indexed Postgres column; the secret half is verified via constant-time SHA-256 hash equality (subtle::ConstantTimeEq). Timing side-channel attacks against bearer-token validation get nothing useful.