Authentication
API keys, environments, rotation, and why every failure looks the same.
Auvral authenticates your backend with a single secret key sent in the x-api-key
header:
POST /v1/sessions HTTP/1.1
x-api-key: sk_auvral_test_9Kf2mQ...
Content-Type: application/jsonThere is no OAuth handshake, no token exchange, no login endpoint. The key is the whole credential.
Keys are per-organization and per-environment
Your organization has exactly one active key per environment, one sandbox and one live. They are not a list you add to; they are two slots you rotate.
| Prefix | Environment | Behaviour |
|---|---|---|
sk_auvral_test_ | Sandbox | Full flow, no real verifications produced |
sk_auvral_live_ | Live | Real users, real verdicts |
There is no environment parameter on any request. The key you use is the environment you are in, and it also decides which organization the session belongs to and which flows it may reference.
Where keys come from
Dashboard, Integrations, API keys.
- Sandbox keys can be revealed at any time. Click Show whenever you need the value again.
- Live keys are shown exactly once, at the moment they are created or rotated. Auvral stores only a hash and cannot recover the value. If you lose it, rotate to get a new one.
A new organization has a live key that has never been revealed. The dashboard says so and offers a rotation, which is safe at that point because nothing is using it yet.
Rotation
Click Rotate on the environment you want to replace.
Rotation is immediate and not overlapping: the old key stops working the moment the new
one is issued. There is no grace period and no second active key, so anything still sending
the old key starts receiving 401 at once.
So for a live rotation, sequence it:
Plan for a short window where in-flight requests fail. Sessions you created before the rotation are unaffected, and users mid-verification carry on normally.
Rotate whenever a key may have been exposed: a leaked log, a commit, someone leaving the team.
Keep the key on your server
The API key authenticates as your entire organization. Anyone holding it can create sessions and consume your verification quota.
It belongs in exactly one place: your backend's environment configuration. It must never reach:
- browser JavaScript, or any bundle shipped to a browser
- a mobile app binary
- a URL, a query string, or a redirect
- a public repository, a CI log, or an error report
The verification flow never needs it. Your backend exchanges the key for a url scoped
to one user, and only that URL goes to the browser. If you find yourself wanting the key
client-side, put POST /v1/sessions behind your own authenticated
route and return only the url.
Every failure returns the same 401
{ "statusCode": 401, "message": "Invalid api key", "code": "INVALID_API_KEY" }You get this exact response when the key is missing, empty, malformed, has an unknown prefix, does not exist, has been revoked, or is being used against the wrong environment.
This is deliberate: a more specific message would tell someone probing your integration which of their guesses was close.
It does mean the response body will not tell you what went wrong. Work down this list instead:
- Is the header spelled
x-api-key? - Does the value start with
sk_auvral_test_orsk_auvral_live_? - Was it rotated in the dashboard since you last copied it?
- Does this key belong to the same Auvral environment
$AUVRAL_API_URLpoints to? - Is a shell variable expanding to an empty string?
The Last used timestamp on the API keys page is a quick check: if it never updates, the
key never reached Auvral as you expect.
Going live
One thing changes: swap sk_auvral_test_ for sk_auvral_live_. Sandbox and live answer at
the same host, so it is the key prefix that tells Auvral which one you mean, not the base
URL.
This environment's API base URL is https://api.dev.auvral.com
Flow ids differ per environment, so check you are referencing a flow your live organization
actually has. A sandbox flow id sent with a live key returns 404 Flow not found.
Everything else is identical.