Authentication
The Hexolus Payment Gateway authenticates every request with a long-lived
bearer API key. There is no OAuth flow, no session cookie, and no signed
request body — possession of a valid hxk_* token grants full client-scoped
access to the API.
Token format
Tokens follow a fixed three-part layout:
hxk_<8-char-prefix>_<43-char-base64url-random>
| Segment | Length | Purpose |
|---|---|---|
hxk |
3 | Brand prefix. Constant. Lets log scrubbers and secret scanners detect a leaked key by substring. |
| prefix | 8 | Random; stored in plaintext in our database as a safe public identifier. Surfaces in audit logs. |
| random | 43 | URL-safe base64 (32 random bytes). Never stored — we keep only SHA-256(full_token) at rest. |
A full token is therefore 56 characters: 3 + 1 + 8 + 1 + 43. An example
(do not use this — it's illustrative):
hxk_a1b2c3d4_VGhpc0lzQVNhbXBsZVRva2VuU3RyaW5nUmFuZG9tQnl0ZXNYWQ
Obtaining a key
API keys are minted by the Hexolus operator on your behalf at:
# Staging
https://staging.hexolus.com/admin/clients/<your-client-id>/api-keys
# Production
https://hexolus.com/admin/clients/<your-client-id>/api-keys
Admin UI lives on the apex host (hexolus.com / staging.hexolus.com); the
api.* subdomains serve the integration API only.
Each newly created key is shown once as plaintext immediately after
generation. After you dismiss the modal, only the prefix and the
SHA-256 hash remain — Hexolus cannot recover the plaintext later. If you
lose a key, request a new one and revoke the old.
This "reveal once" policy is why your integration should pull the key out of
the admin UI directly into your secret manager (or .env on a single-host
deploy) at provisioning time, never via copy/paste into a chat thread.
Authorization header
Every request must include:
Authorization: Bearer hxk_<prefix>_<random>
The header is case-sensitive on the scheme (Bearer, not bearer). Any
other scheme — Basic, Token, ApiKey — is rejected with 401.
curl -sS https://api-staging.hexolus.com/v1/balance \
-H "Authorization: Bearer hxk_a1b2c3d4_VGhpc0lzQVNhbXBsZVRva2VuU3RyaW5nUmFuZG9tQnl0ZXNYWQ"
Error responses
All authentication failures return HTTP 401 Unauthorized with the standard
error envelope:
{ "message": "missing bearer token", "code": "auth" }
message |
Cause |
|---|---|
missing bearer token |
No Authorization header, or scheme was not Bearer. |
empty bearer token |
Authorization: Bearer with no token. |
malformed token |
Token did not parse as hxk_<prefix>_<tail>. |
invalid credentials |
Prefix not found, OR prefix found but the SHA-256 hash did not match. |
key expired |
The key's expires_at is set and is in the past. |
Hexolus deliberately does not distinguish "unknown prefix" from "wrong tail"
in the public error message — both surface as invalid credentials — so a
caller cannot use response timing or wording to enumerate valid prefixes.
Optional fields
- Expiry. Operators may set an
expires_aton a key. Expired keys 401. Most production keys are non-expiring. - Scopes. Keys carry a JSON
scopesarray. The current public API has a single implicit scope (*); future scope-restricted keys will be additive and backward compatible. - last_used_at. On every successful authenticated request, Hexolus
asynchronously updates
last_used_atfor the key. This is visible in the admin UI and is useful for detecting unused (rotatable) keys.
Best practices
- Store secrets in a secret manager (AWS Secrets Manager, Vault, Doppler, Kubernetes Secrets, etc.). Do not commit them to git.
- Never log the full token. It is safe to log only the prefix (the first
12 characters:
hxk_<8-char-prefix>). Hexolus's own logs follow this rule. - Rotate at least every 90 days. Provision a new key, deploy it to all workloads, then revoke the old one. There is no rolling-window grace — a revoked key 401s immediately.
- One key per workload. If you run staging + production + a CI job, mint three separate keys so you can revoke a leaked CI key without impacting production traffic.
- Use HTTPS only. Hexolus refuses plaintext HTTP. Pinning is optional but encouraged for mobile / desktop clients.
- Treat 401 as terminal. Do not retry an authentication failure — it indicates a configuration problem, not a transient one. See Errors for the retryable codes.