# Health & version probes

> Bare-path liveness, readiness, and build-fingerprint endpoints for Kubernetes, Fly.io, and uptime monitoring.

Source: https://docs.hatched.live/docs/reference/health-version

Hatched exposes three top-level probe paths intended for ops tooling.
They serve the same purpose as the prefixed `/api/v1/health/*`
endpoints — but they are separate handlers, not the same backend, and
they skip the API prefix so K8s/Fly probes work out of the box without
remapping paths. The human-readable multi-component report lives at
`GET /api/v1/health`.

| Endpoint | Purpose | Status code |
| --- | --- | --- |
| `GET /healthz` | Liveness — "the process is up" | always 200 |
| `GET /readyz` | Readiness — DB + Redis reachable | 200 ready, 503 not ready |
| `GET /version` | Build fingerprint | always 200 |

The detailed multi-component breakdown (queues, image provider, latency
per dependency) still lives at `GET /api/v1/health` for dashboards or
on-call debugging.

## `GET /healthz`

```bash
curl https://api.hatched.live/healthz
# {"ok":true,"uptime":4623}
```

Returns immediately. Uptime is in seconds since process start. Use this
as your Kubernetes `livenessProbe` — restart the container only if the
process is genuinely wedged.

## `GET /readyz`

```bash
curl -i https://api.hatched.live/readyz
# HTTP/1.1 200 OK
# {"ready":true,"db":true,"redis":true}
```

Returns `503` with `{"ready": false, ...}` if Postgres or Redis is
unreachable. Use as the Kubernetes `readinessProbe` so the pod is removed
from the load balancer until both deps are green.

## `GET /version`

```bash
curl https://api.hatched.live/version
# {
#   "version": "1.42.0",
#   "git_sha": "a1b2c3d4",
#   "built_at": "2026-05-25T09:14:30Z",
#   "node_version": "v20.11.0"
# }
```

Useful as a deployment fingerprint:

- Diff `git_sha` between expected and actual to confirm a rollout
  succeeded across all instances.
- Compare the SDK consumer's `client.auth.whoami()` plan against the
  API's `version` when reproducing a bug — "I'm on 1.42.0 with plan
  growth" is a lot easier to triage than "I think it broke yesterday."

The three env vars `API_VERSION`, `GIT_SHA`, and `BUILT_AT` are injected
at build time by the deployment pipeline. Either may be `"unknown"` on
local dev or in test environments where the build step is skipped.

## Why two paths per probe

`/api/v1/health/live` (prefixed) and `/healthz` (bare) are separate
handlers serving the same purpose — not the same handler. SDK calls and
human dashboards use the prefixed form; ops tooling uses the bare form.
Removing one of them would break either the SDK contract or the standard
probe playbook — both are kept stable.

Their response shapes and readiness semantics also differ, so do not
treat them as interchangeable:

- Liveness: bare `/healthz` returns `{"ok":true,"uptime":...}`, while
  prefixed `/api/v1/health/live` returns `{"alive":true,"uptime":...}`.
- Readiness: bare `/readyz` checks only DB + Redis, while prefixed
  `/api/v1/health/ready` additionally checks the queue and image
  provider (returning `{"ready":...,"checks":{...}}`). The prefixed
  endpoint can therefore report `503` while bare `/readyz` still
  reports `200`.
