Authentication

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.


Why API key + JWT?

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:


Obtaining an API key

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.


Calling POST /v1/auth/token

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

JWT contents

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.


Token lifetime and re-issuance

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:

  1. Issue a token at startup.
  2. On each outbound request, check whether the token will expire within the next 60 seconds. If so, re-issue before making the call.
  3. On receiving 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.


Signing-key rotation

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).


Troubleshooting 401 errors

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

Security guidance