Authentication

Bearer tokens with Stripe-style prefixes; SHA-256 hashed at rest.

Key format

Each issued key looks like this:

qgre_live_a1b2c3d4e5f6f0e1d2c3b4a5a1b2c3d4e5f6f0e1d2c3b4a5a1b2c3d4e5f6f0e1
  • qgre_ — type prefix (always)
  • live_ or test_ — environment. test_ keys are ALWAYS free + sandboxed; they never bill credits or hit the demo daily cap
  • a1b2c3d4e5f6 — first 12 hex chars are the public prefix; safe to log / display
  • f0e1...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.