---
title: "Readiness: database, migrations, provider keys, provider status"
method: GET
path: "/health/deep"
tags: ["meta"]
---

# Readiness: database, migrations, provider keys, provider status

`GET /health/deep`

## Request

- Base URL: `https://api.hookdetector.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.hookdetector.com/health/deep`
- Auth: none declared

For monitoring and for the web: a timed database round trip, whether every
migration this code needs is applied, whether each provider key is configured (never
a value), and provider_status: whether the language model or the search provider is
refusing right now, read from how recent runs ended (cached 30 seconds, never a paid
probe), and for the search provider our own credits_left (read in the background at
most every 10 minutes from its free balance endpoint, never on this request), low
(under HD_SEARCH_CREDITS_LOW) and accepting_runs (false while it refuses us: new runs
are then 503 search_unavailable). A refusing provider does not make this 503; it is a
warning for new runs. fallback_providers says whether the optional anthropic model
fallback is configured (never a value); it never makes this degraded.
commit is the git commit the image was built from ("unknown" outside a deploy), and
credits_today is how much of the global daily research ceiling is reserved (UTC day).
database.state is "up", "busy" or "down". Busy means every pooled connection is in
use: it answers 200 with status ok, the schema as last read, and no provider_status.

## Response `200`

Successful Response

- HealthDeepOut
  - `status` string, required
  - `version` string, required
  - `database` object, required
  - `schema` object, required
  - `providers` object, required
  - `fallback_providers` object, nullable: optional providers, configured or not (never a value): anthropic, the model fallback. Unlike providers, a missing one never makes status degraded
  - `provider_status` ProviderStatusOut, required
    - `model` ProviderOut, required
      - `state` union, required
        - 'ok' | 'refusing' | 'unknown'
        - string
      - `last_failure_at` string, nullable, required
      - `last_success_at` string, nullable, required
      - `message` string, nullable, required
      - `credits_left` integer, nullable: search only: our credits left with the search provider, from its free balance endpoint read at most every 10 minutes; null until read
      - `credits_checked_at` string, nullable: search only: when credits_left was read (ISO 8601)
      - `low` boolean, nullable: search only: credits_left is under low_threshold (about a day of use); null until read
      - `low_threshold` integer, nullable: search only: HD_SEARCH_CREDITS_LOW
      - `accepting_runs` boolean, nullable: search only: false while the provider refuses us (HTTP 402, 401 or 403); a new run is then 503 search_unavailable, nothing reserved
    - `search` ProviderOut, required
      - `state` union, required
        - 'ok' | 'refusing' | 'unknown'
        - string
      - `last_failure_at` string, nullable, required
      - `last_success_at` string, nullable, required
      - `message` string, nullable, required
      - `credits_left` integer, nullable: search only: our credits left with the search provider, from its free balance endpoint read at most every 10 minutes; null until read
      - `credits_checked_at` string, nullable: search only: when credits_left was read (ISO 8601)
      - `low` boolean, nullable: search only: credits_left is under low_threshold (about a day of use); null until read
      - `low_threshold` integer, nullable: search only: HD_SEARCH_CREDITS_LOW
      - `accepting_runs` boolean, nullable: search only: false while the provider refuses us (HTTP 402, 401 or 403); a new run is then 503 search_unavailable, nothing reserved
    - `refusing` boolean, required: true when either provider reads refusing
    - `window_minutes` integer, required
    - `source` string, required
  - `model` object, nullable: the language model plan, names only: {"primary": "anthropic" or "openai", "name" (e.g. claude-haiku-5-5), "effort" (e.g. xhigh, null for OpenAI), "backup", "speech": the provider that hears dialect clips first, "speech_backup": the one behind it, "anthropic_keys": how many Anthropic keys are in the chain, "anthropic_keys_spent": the chain positions (1 is the main key) this process found out of credit or refused in the last 10 minutes, positions only}
  - `commit` string, nullable: the git commit this image was built from; "unknown" outside a deploy
  - `credits_today` CreditsTodayOut
    - `day` string, required: the UTC day, YYYY-MM-DD
    - `reserved` integer, required: credits reserved by every run started this UTC day
    - `ceiling` integer, required: past this, new runs are refused with 503 at_capacity until the next UTC midnight

## Other responses

- `503`: degraded: database down, a migration missing or a provider key not configured

---

[API](https://skmtc.dev/hookdetector/apis/hook-detector.md) · [All operations](https://skmtc.dev/hookdetector/apis/hook-detector/llms.txt) · [OpenAPI document](https://skmtc.dev/hookdetector/apis/hook-detector/revisions/b136a2804d2b?raw)
