Fontes Portal API uses a two-layer credential scheme: a long-lived API key that identifies your integration, and a short-lived JWT that authorises individual requests. The two-layer design separates identity from session tokens, making it easy to rotate API keys independently of running integrations and to revoke access without deploying code changes.
A plain API key on every request is simple but makes rotation painful — every request would need the new key the moment the old one is revoked. A plain OAuth client-credentials flow adds complexity (token introspection endpoint, client secrets, scopes). The Fontes approach is a deliberately minimal middle ground:
API keys are issued by Fontes support. Contact api-support@fontes.ee to request one. Include the name of the integration system and the organisation responsible for it.
Keys look like fpk_live_<random>. Store them in your secret manager — never
in source code, configuration files committed to version control, or logs.
Pass the API key in the X-Api-Key header. No request body is required.
Request
POST /v1/auth/token HTTP/1.1
Host: api.fontes.ee
X-Api-Key: fpk_live_...
Successful response (200)
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"token_type": "Bearer",
"expires_in": 900
}
Error responses
| Status | Code | Cause |
|---|---|---|
| 401 | unauthorized |
API key not recognised or revoked |
| 403 | inactive_client |
Integration exists but is disabled |
| 429 | rate_limit_exceeded |
More than 10 token requests per minute from this IP |
The JWT payload contains a small set of public claims:
| Claim | Type | Description |
|---|---|---|
sub |
string | Internal client identifier |
cli |
string | Human-readable integration name |
iat |
number | Issued-at timestamp (Unix seconds) |
exp |
number | Expiry timestamp (Unix seconds, iat + 900) |
Do not rely on the JWT payload for business logic — treat it as an opaque session token. The server validates it on every request.
JWTs expire after 15 minutes (expires_in: 900). There is no refresh
token. When a request returns 401, simply call POST /v1/auth/token again
with the same API key to get a fresh JWT.
Recommended client pattern:
401 from any endpoint (e.g., due to clock skew or a
particularly slow response), re-issue once and retry the failed request.The absence of a refresh token is intentional. Long-lived API keys combined with short-lived JWTs achieves the security properties of refresh tokens without the added complexity of a three-token flow.
Fontes rotates JWT signing keys on the server periodically. During a rotation window, both the old and new keys are accepted. After the window closes, only the new key signs tokens. Integrators do not need to take any action for server-side key rotation — just re-issue a token when the old one expires.
Integrator action is required only when your own API key is rotated. In that case, update the key in your secret manager and deploy. Existing JWTs issued against the old API key remain valid until they expire naturally (up to 15 minutes).
If POST /v1/salary-rows (or another protected endpoint) returns 401:
| Root cause | How to identify | Action |
|---|---|---|
| Expired JWT | exp claim in the past |
Re-issue a token |
| Clock skew | Server and client clocks differ | Sync your system clock (NTP); skew > 5 min is rejected |
| Revoked API key | Support confirms revocation | Request a replacement key |
| Inactive client | inactive_client code from /auth/token |
Contact Fontes support |
| Wrong audience | JWT was issued for a different environment | Ensure you call the correct environment's token endpoint |
traceId from error responses
instead.