invalid_api_key
HTTP 401 type: authentication_error param: Authorization{ "error": { "type": "authentication_error", "code": "invalid_api_key", "message": "No API key was recognised. Send `Authorization: Bearer fh_live_…` or `Authorization: Bearer fh_test_…`.", "param": "Authorization", "upstream": null, "doc_url": "https://docs.fiskhub.com/errors/invalid_api_key" }}The response also carries WWW-Authenticate: Bearer.
What happened
The Authorization header was missing, malformed, or carried a key we do not recognise. The
request stopped at authentication; nothing was read, written or fiscalized.
Common causes:
- No
Authorizationheader at all. - The scheme omitted —
Authorization: fh_live_…instead ofAuthorization: Bearer fh_live_…. - The key truncated by an environment variable that was not quoted, or by copy-paste.
- Whitespace or a newline inside the key.
- A key that was deleted rather than revoked, or one from a different environment.
Should you retry?
Not with the same key. Authentication is deterministic; the same key fails the same way forever.
The fix
curl -sS https://api.fiskhub.com/v1/invoices \ -H "Authorization: Bearer fh_live_9tK2xQ7mA4vD8nR1sB6yE3wZ0pL5cH" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ … }'Check, in order:
- The header name and scheme. Exactly
Authorization: Bearer <key>, one space. - The prefix. Every key starts
fh_live_orfh_test_. Anything else was not issued by us. - The whole key made it. Print its length and compare — a shell variable that lost its quoting is the classic cause.
- It is the right environment’s key. A staging key against production, or the reverse.
Verify it against an endpoint that does nothing:
curl -sS -o /dev/null -w "%{http_code}\n" \ https://api.fiskhub.com/v1/companies \ -H "Authorization: Bearer $FISKHUB_API_KEY"200 means the key is fine and the problem is elsewhere.
Lost keys are replaced, not recovered
Keys are shown once, at creation, and stored only as a SHA-256 hash. There is no way to read one back. Issue a new key in the dashboard and revoke the old one.
If the key still exists but is refused, see api_key_revoked — a
revoked key returns that code, not this one.