API Authentication¶
By default the Chap Core API has no authentication. This is intentional: the supported deployments either run DHIS2 and Chap Core together in one containerised setup, or run both locally during development. In both cases the API is reachable only from the machine or the internal network, and DHIS2 enforces authorization in front of it via the Route API.
If you need to expose Chap Core beyond that -- for example connecting a cloud DHIS2 instance to a local Chap Core through a tunnel -- you can enable an opt-in API token. It is the bare minimum needed to put the API somewhere it can be reached from the internet, and it should always be combined with HTTPS.
A token is not a substitute for network isolation
The token is a single shared secret with no rotation, no expiry and no per-client identity. Keeping Chap Core off the public internet is still the recommended setup. See Recommendation for server deployment.
Enabling the token¶
Set the CHAP_API_TOKEN environment variable on the Chap Core server. When it is unset,
authentication is disabled and the API behaves exactly as before.
Generate a token with any of these:
uuidgen is available on macOS and on Linux via util-linux, but is often missing from
slim container images -- openssl is the more portable option.
Use at least 32 characters. The API has no rate limiting, so a short token can be guessed by anyone who can reach the port. Chap Core logs a warning at startup if the token is shorter than that, but still starts.
With Docker Compose, add it to your .env file and it is passed through to the chap
service:
Then restart the stack:
Making authenticated requests¶
Send the token as a bearer token in the Authorization header:
Chap Core also accepts Authorization: ApiToken your-generated-token, as sent by DHIS2
routes using auth.type = api-token. Both schemes use the same CHAP_API_TOKEN value.
Scheme names are case-insensitive; the token must match exactly.
Which endpoints require the token¶
When CHAP_API_TOKEN is set, every API endpoint requires it except the ones below.
| Path | Auth | Why |
|---|---|---|
/health |
Public | Liveness probe. Called without headers by the container healthcheck and Kubernetes. |
/health/ready |
Public | Readiness probe, same reason. |
/system/info |
Public | Lets clients discover whether a token is required before authenticating. |
/docs, /redoc, /openapi.json |
Public | The API reference. It describes an open-source API, so it discloses nothing. |
| Everything else | Required | Includes /v1/** and /v2/**. |
To call the API from /docs, click Authorize, paste the token, and "Try it out" then
sends it as Authorization: Bearer <token>.
Service registration is covered too, with one accommodation for chapkit -- see Relation to the service registration key.
Error responses¶
When CHAP_API_TOKEN is configured:
| Scenario | Response |
|---|---|
Missing Authorization header |
401 Unauthorized |
Wrong token, or a scheme other than Bearer or ApiToken |
401 Unauthorized |
Both cases return the same body and a WWW-Authenticate: Bearer header, so a client can
tell "this server wants a token" apart from a network failure:
Detecting whether a server requires a token¶
/system/info stays public and reports auth_required, so a client can check before it has
a token:
{
"chap_core_version": "2.2.0",
"python_version": "3.13.0",
"server_date": "2026-01-01T12:00:00+00:00",
"server_time_zone_id": "Etc/UTC",
"revision": "",
"auth_required": true
}
Connecting from the Modeling App¶
The Modeling App reaches Chap Core through a DHIS2 Route. Configure the route's encrypted authentication with the CHAP token:
DHIS2 sends Authorization: ApiToken your-generated-token to Chap Core. Use the value of
CHAP_API_TOKEN, not a DHIS2 personal access token. Existing routes sending a Bearer token
continue to work. See the
DHIS2 Route API documentation
for the headers and auth fields.
Relation to the service registration key¶
CHAP_API_TOKEN and SERVICEKIT_REGISTRATION_KEY are separate secrets, configured and
checked independently, and either can be enabled without the other. See
Service Registration.
Self-registering chapkit services need special handling because servicekit can only send the
X-Service-Key header -- it has no way to send Authorization. The API token is therefore
accepted in X-Service-Key as well, on any path, and on the /v2/services paths a
configured registration key is accepted there too:
| Server configuration | What a chapkit service sends |
|---|---|
Only CHAP_API_TOKEN |
SERVICEKIT_REGISTRATION_KEY set to the API token |
Only SERVICEKIT_REGISTRATION_KEY |
SERVICEKIT_REGISTRATION_KEY set to the registration key |
| Both | SERVICEKIT_REGISTRATION_KEY set to either one |
A registration key is only accepted on /v2/services paths. It cannot be used as a
general-purpose credential against /v1/**, so a model service cannot read or delete
datasets with it.
Other clients -- the Modeling App, curl, the Python SDK -- should use
Authorization: Bearer or Authorization: ApiToken.