Odit Verify

Health / Status API

Two open endpoints for monitors and dashboards — a single-purpose health probe and a structured per-component status snapshot.

Both endpoints are open (no API key required), cached for ~15 s at the edge plus 30 s in-process so a flood of probes never burns through to the underlying checks, and safe to hit from any host (no rate-limit attribution).

GET /api/verify — minimal health

Use when all you need is "is v.odit.et alive". Always returns 200 with a tiny JSON body.

curl https://v.odit.et/api/verify
{ "ok": true, "ts": "2026-06-11T19:23:11.482Z" }

Don't use this to check whether an upstream (telebirr, CBE, …) is healthy — this endpoint only reports on v.odit.et itself.

GET /api/status — structured per-component status

Use when you want to know which upstream is degraded or what response times look like right now.

curl https://v.odit.et/api/status

Response

HTTPWhen
200Overall status is operational or slow
503Overall status is down (at least one component returned a 5xx or timed out)
{
  "ok": true,
  "status": "operational",
  "checkedAt": "2026-06-11T19:23:11.482Z",
  "cached": true,
  "components": [
    {
      "name": "verify-web (this service)",
      "host": "v.odit.et",
      "group": "internal",
      "status": "operational",
      "responseMs": 0,
      "httpStatus": 200,
      "error": null
    },
    {
      "name": "Postgres (auth + API keys)",
      "host": "127.0.0.1:5432",
      "group": "internal",
      "status": "operational",
      "responseMs": 35,
      "httpStatus": null,
      "error": null
    },
    {
      "name": "Telebirr — transactioninfo.ethiotelecom.et",
      "host": "transactioninfo.ethiotelecom.et",
      "group": "upstream",
      "status": "operational",
      "responseMs": 522,
      "httpStatus": 200,
      "error": null
    }
  ]
}

Field reference

FieldTypeNotes
okbooleantrue unless overall status is down
statusenumoperational | slow | down — worst across all components
checkedAtISO timestampWhen the underlying probe round was last taken
cachedbooleantrue if the snapshot came from the in-process 30 s cache
components[].namestringHuman label (e.g. "Telebirr — transactioninfo.ethiotelecom.et")
components[].hoststringUpstream host or internal address
components[].groupenuminternal (the v.odit.et service itself) or upstream
components[].statusenumPer-component operational / slow / down
components[].responseMsnumber | nullProbe round-trip time
components[].httpStatusnumber | nullHTTP status the upstream returned (or null for non-HTTP probes like Postgres)
components[].errorstring | nullNetwork or timeout error message when the probe failed

Classification rules (same as the human-readable /status page)

  • down — probe failed or upstream returned HTTP >= 500
  • slow — probe succeeded but took longer than the per-component slow threshold (typically 1.5 s)
  • operational — probe succeeded under threshold

HEAD /api/status — uptime-monitor friendly

Same status code semantics as GET /api/status, no body. Cheapest possible probe — recommended for monitors that only need to react to 200 vs 503.

curl -I https://v.odit.et/api/status
# HTTP/2 200
# cache-control: public, max-age=15, s-maxage=15

Polling cadence

The in-process snapshot refreshes every 30 s and the public response is cache-controlled for 15 s. A polling interval between 30 s and 5 minutes is plenty — anything faster gets you the same cached snapshot, just with extra bandwidth.

On this page