> ## Documentation Index
> Fetch the complete documentation index at: https://docs.theroyalglow.in/llms.txt
> Use this file to discover all available pages before exploring further.

# Health

> Liveness and dependency-check endpoint for uptime monitors, CI, and load balancers.

# Health

A liveness/health probe used by BetterStack, CI, and load balancers.

<Callout type="info">
  **Base URL:** `https://theroyalglow.in` · **Auth:** Public. Unlike every other
  endpoint, `GET /api/health` does **not** use the `{ success, data }` envelope —
  it returns its own documented `HealthStatus` contract and its own status codes.
  The response always sends `Cache-Control: no-store` and an `X-Health-Status`
  header. The admin app exposes an identical probe at
  `admin.theroyalglow.in/api/health`; this page documents the customer probe on
  `theroyalglow.in`.
</Callout>

## GET /api/health

Runs three independent dependency checks (database, Redis, R2) in parallel and
reports an overall status. The handler is fully guarded — each check is wrapped
so the endpoint never throws.

**Minimum role:** Public

```http theme={null}
GET /api/health
```

This endpoint takes no parameters.

### Response shape

```json theme={null}
{
  "status": "healthy",
  "timestamp": "2026-06-02T09:20:00.000Z",
  "version": "a1b2c3d",
  "uptime": 12345.67,
  "checks": {
    "database": { "status": "pass", "latencyMs": 12 },
    "redis": { "status": "skip", "latencyMs": 0, "message": "Redis not configured" },
    "r2": { "status": "skip", "latencyMs": 0, "message": "R2 not configured" }
  }
}
```

### Response fields

<TypeTable
  type={{
status: {
  type: 'enum',
  description: 'Overall status: healthy, degraded, or unhealthy.',
  required: true,
},
timestamp: {
  type: 'string',
  description: 'ISO-8601 timestamp of the check.',
  required: true,
},
version: {
  type: 'string',
  description: 'Commit SHA from COMMIT_SHA, or "unknown" if unset.',
  required: true,
},
uptime: {
  type: 'number',
  description: 'Process uptime in seconds (0 if unavailable).',
  required: true,
},
'checks.database': {
  type: 'object',
  description: 'The database component check: { status, latencyMs, message? }.',
  required: true,
},
'checks.redis': {
  type: 'object',
  description: 'The Redis component check: { status, latencyMs, message? }.',
  required: true,
},
'checks.r2': {
  type: 'object',
  description: 'The R2 component check: { status, latencyMs, message? }.',
  required: true,
},
}}
/>

Each component check is `{ status: "pass" | "fail" | "skip", latencyMs: number, message?: string }`.

<Accordions type="single">
  <Accordion title="pass — dependency responded successfully">
    The dependency responded successfully within the check window.
  </Accordion>

  <Accordion title="fail — configured but unreachable or errored">
    The dependency is configured but unreachable or errored. A `message` explains why.
  </Accordion>

  <Accordion title="skip — not configured, so not checked">
    The dependency is not configured, so it was not checked. **Redis** reports
    `skip` when `UPSTASH_REDIS_REST_URL`/`UPSTASH_REDIS_REST_TOKEN` are unset; **R2**
    reports `skip` when `NEXT_PUBLIC_R2_PUBLIC_URL` is unset.
  </Accordion>
</Accordions>

### Overall status rules

The **database is the only hard dependency**. A `skip` never degrades the
overall status.

| Condition                                      | Overall `status` | HTTP code |
| ---------------------------------------------- | ---------------- | --------- |
| `database.status === "fail"`                   | `unhealthy`      | **503**   |
| Database passes, but `redis` or `r2` is `fail` | `degraded`       | **200**   |
| Database passes; Redis/R2 `pass` or `skip`     | `healthy`        | **200**   |

So a no-keys local/dev environment (Redis and R2 unconfigured → `skip`) reports
`healthy` with a `200`. Only a failing database produces a `503`.

### Status examples

<Tabs items={['Healthy', 'Degraded', 'Unhealthy']}>
  <Tab value="Healthy">
    Redis and R2 unconfigured (`skip`); database passes → `200`.

    ```json theme={null}
    {
      "status": "healthy",
      "timestamp": "2026-06-02T09:20:00.000Z",
      "version": "a1b2c3d",
      "uptime": 12345.67,
      "checks": {
        "database": { "status": "pass", "latencyMs": 12 },
        "redis": { "status": "skip", "latencyMs": 0, "message": "Redis not configured" },
        "r2": { "status": "skip", "latencyMs": 0, "message": "R2 not configured" }
      }
    }
    ```
  </Tab>

  <Tab value="Degraded">
    Redis configured but unreachable; database still passes → `200`.

    ```json theme={null}
    {
      "status": "degraded",
      "timestamp": "2026-06-02T09:20:00.000Z",
      "version": "a1b2c3d",
      "uptime": 12345.67,
      "checks": {
        "database": { "status": "pass", "latencyMs": 12 },
        "redis": { "status": "fail", "latencyMs": 51, "message": "Redis unreachable" },
        "r2": { "status": "skip", "latencyMs": 0, "message": "R2 not configured" }
      }
    }
    ```
  </Tab>

  <Tab value="Unhealthy">
    Database down → `503`.

    ```json theme={null}
    {
      "status": "unhealthy",
      "timestamp": "2026-06-02T09:20:00.000Z",
      "version": "a1b2c3d",
      "uptime": 12345.67,
      "checks": {
        "database": { "status": "fail", "latencyMs": 2003, "message": "DB unreachable" },
        "redis": { "status": "skip", "latencyMs": 0, "message": "Redis not configured" },
        "r2": { "status": "skip", "latencyMs": 0, "message": "R2 not configured" }
      }
    }
    ```
  </Tab>
</Tabs>

### Headers

<TypeTable
  type={{
'Cache-Control': {
  type: 'string',
  description: 'Always no-store.',
  required: true,
},
'X-Health-Status': {
  type: 'string',
  description: 'The overall status (healthy / degraded / unhealthy).',
  required: true,
},
}}
/>


## Related topics

- [Deployment](/content/docs/deployment.md)
- [Observability](/content/docs/observability.md)
- [Pages & Routes](/content/docs/pages-and-routes.md)
- [API Reference](/content/docs/api-reference.md)
