Health Endpoints
Three endpoints report whether the API is up, whether it is ready to take traffic, and whether the process is alive.
/api/v1, and they take no API keyThe health paths are served at the root of the API, outside /api/v1, and they are the
only paths excluded from the API-key security scheme. Sending a key is harmless; omitting one
changes nothing. Because of that, a 200 from any of them says nothing at all about whether
your credentials are valid โ use an authenticated endpoint for that.
Which one do I call?โ
| Question | Endpoint | Notes |
|---|---|---|
| Is the API up, and which environment is this? | GET /health | Reports environment; the fastest way to tell test from production |
| Should this instance receive traffic? | GET /health/ready | 200 when ready, 503 when not. Wire load balancers and uptime monitors here |
| Is the process alive? | GET /health/live | Always 200. Wire container orchestrator liveness probes here |
GET /healthโ
Basic health check. Unauthenticated.
curl -X GET https://app.goroute.ai/peppol-api/health
{
"status": "healthy",
"version": "1.0.0",
"environment": "production",
"timestamp": "2026-08-06T06:09:57.050723"
}
| Field | Meaning |
|---|---|
status | "healthy" |
version | API version |
environment | Which environment this base URL and deployment resolve to |
timestamp | Server time the response was generated |
Use environment to confirm which environment you are talking to before you go live. It is
the field the Environments page tells you to check.
GET /health/readyโ
Readiness. Answers 200 when ready and 503 when not ready โ a probe that always
returned 200 could never take a half-initialised instance out of rotation. An instance that can
serve but has something an operator should fix answers 200 with "status": "degraded".
curl -X GET https://app.goroute.ai/peppol-api/health/ready
Production answered this on 5 November 2026:
{
"status": "ready",
"version": "1.0.0",
"environment": "production",
"checks": {
"database": "connected",
"redis": "connected"
},
"cell_invariants": [],
"rule_files": {
"ae_tdd": {"status": "ok"},
"de_kosit_scenarios": {"status": "ok"},
"edec_mls_outbound": {"status": "ok"},
"fr_br_fr": {"status": "ok"},
"fr_cdar": {"status": "ok"},
"fr_extended_ctc_fr": {"status": "ok"},
"fr_facturx_basicwl": {"status": "ok"},
"fr_facturx_en16931": {"status": "ok"},
"fr_facturx_extended": {"status": "ok"},
"pint_ae_c2_outbound": {"status": "ok"},
"pint_ae_c3_inbound": {"status": "ok"},
"pint_om_c3_inbound": {"status": "ok"},
"zatca_sdk_3_4_8": {"status": "ok"}
},
"timestamp": "2026-11-05T06:31:53.677183"
}
| Field | Meaning |
|---|---|
status | "ready"; "degraded" when the database is connected but cell_invariants is not empty or a rule set in rule_files is not "ok" (still HTTP 200); "not_ready" when the database check failed (HTTP 503) |
version | API version |
environment | Which environment this deployment resolves to |
checks.database | "connected", or an error string when the database check failed |
checks.redis | "connected", "not_checked", or an error string |
cell_invariants | A list of findings, each {"check": "<name>", "detail": "<what is wrong and what to do>"}. Empty when there is nothing to report |
rule_files | One entry per rule set the validators read, keyed by its name: {"status": "ok"}, {"status": "missing", "missing": [...]} naming the files the image lacks, or {"status": "unreadable", "detail": "..."}. If the list of rule sets itself cannot be read, the map holds a single _manifest entry with "status": "unreadable" |
timestamp | Server time the response was generated |
Findings in cell_invariantsโ
On GoRoute's shared API the list is usually empty. It can hold:
mls_core_identity_missingoredec_mls_rules_missing, when the eDEC MLS service is switched on and not set up completely;shipping_credential_refused, on a service where GoRoute Shipping is switched on, when a carrier refused that service's partner key at the latest check. The check runs shortly after the service starts and then daily. Only the instance running GoRoute's scheduled jobs reports this finding, so one answer can show it and the next not. It means GoRoute must correct the key: until then, no customer can connect that carrier or ship with it.
A dedicated cell (a separate GoRoute installation for one operator) can also report:
message_id_domain_default,notify_addresses_platform,cell_brand_default,cell_brand_incompleteandcell_brand_platform_value: a GoRoute default the cell's operator was meant to replace;codelists_emptyandcodelists_unreadable: the Peppol code lists are not loaded or cannot be read;org_hierarchy_path_nullandorg_hierarchy_path_unreadable: organisations the operator console cannot list.
Each finding's detail says what to fix.
What to alert onโ
- HTTP
503("status": "not_ready"): take the instance out of rotation. This is what a load balancer or readiness probe should act on. "status": "degraded": an operator should look atcell_invariantsandrule_files. The instance keeps serving. A monitor that alerts on anystatusother than"ready"fires on it; one that watches the HTTP code alone does not.
A Redis failure degrades rate limiting but does not make an instance unready. checks.redis
can therefore report an error string while the endpoint still answers 200 with
"status": "ready". Do not alert on checks.redis alone, and do not treat a 200 as proof
that Redis is healthy. Readiness is decided by checks.database and nothing else: it alone
chooses between 200 and 503.
This endpoint previously answered 200 in every case, including when the database check
failed. It now answers 503 when it is not ready. If you wired a load balancer, an uptime
monitor or an orchestrator readiness probe against the old behaviour and treated any response
as healthy, revisit it โ instances will now be pulled from rotation when the database check
fails, which is the intended behaviour.
GET /health/liveโ
Liveness. Always 200.
curl -X GET https://app.goroute.ai/peppol-api/health/live
{
"status": "alive",
"timestamp": "2026-08-06T06:09:20.524903"
}
It deliberately runs no dependency checks: reaching the handler proves the process is
serving. Failing a liveness probe because a dependency is merely not ready yet would
restart-loop otherwise-healthy instances, and that judgement belongs to /health/ready.
Recommended wiringโ
| Consumer | Endpoint | Treat as failure when |
|---|---|---|
| Load balancer target group | /health/ready | non-200 |
| Uptime / status monitoring | /health/ready | non-200 |
| Container orchestrator liveness probe | /health/live | non-200 |
| Environment confirmation in a deploy script | /health | environment is not what you expected |
Relatedโ
- Environments โ confirming test versus production
- First API Call โ where
/healthfits in the first few requests - Support โ diagnosing an authentication problem, which these endpoints cannot do for you