---
title: Maintenance API
description: "Agent health monitoring, restart, instance state, and factory reset endpoints"
---

# Maintenance API

Check agent health, query runtime state, and trigger maintenance restarts. These endpoints operate on the authenticated user's deployed agent instance.

All lifecycle endpoints (start, stop, restart, repair, reset-memory, update, and `POST /api/openclaw/maintenance`) resolve the target Railway service using a two-tier strategy:

1. **Persisted service ID (preferred)** — when the agent's configuration includes a `runtimeServiceId`, the platform uses it directly without making any Railway API calls. This is the default for agents provisioned with the current platform version.
2. **Project-level discovery (fallback)** — when no persisted service ID is available (for example, agents provisioned before this feature was introduced), the platform falls back to listing all services in the Railway project and matching by name.

## Get instance runtime state

```http
GET /api/instance/:userId
```

Requires session authentication. Returns runtime state for a specific agent instance using the shared runtime probe, which checks `/healthz`, `/readyz`, and `/api/status` on the agent. This endpoint resolves status without a backend hop — it reads the persisted OpenClaw URL from the database and probes the agent in-process.

### Path parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `userId` | string | The instance identifier (matches the user's `openclawInstanceId`) |

### Response

```json
{
  "userId": "abc123",
  "status": "running",
  "statusReason": null,
  "probeChecks": [
    { "path": "/healthz", "ok": true, "status": 200, "reason": null },
    { "path": "/readyz", "ok": true, "status": 200, "reason": null },
    { "path": "/api/status", "ok": true, "status": 200, "reason": null }
  ],
  "startedAt": "2026-04-04T03:10:00.000Z",
  "subdomain": "agentbot-agent-abc123-production.up.railway.app",
  "url": "https://agentbot-agent-abc123-production.up.railway.app",
  "plan": "solo",
  "openclawVersion": "2026.4.11",
  "ffmpegAvailable": true,
  "ffmpegVersion": "ffmpeg version 6.1",
  "provisionedAt": "2026-04-01T00:00:00.000Z",
  "lastSeenAt": "2026-04-04T03:10:00.000Z",
  "gatewayProcessStatus": "active",
  "subscriptionStatus": "active"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `userId` | string | The instance identifier from the request path |
| `status` | string | Runtime status derived from the shared probe: `running`, `healthy`, `starting`, `stopped`, `setup`, `unknown`, or `unreachable`. See [runtime status classification](/api-reference/health#runtime-status-classification) for details. |
| `statusReason` | string \| null | Human-readable explanation when the status is not `running` or `healthy`. For example, `Legacy health probes unavailable; using /api/status`. |
| `probeChecks` | array | Per-probe results for `/healthz`, `/readyz`, and `/api/status`. Each entry includes `path`, `ok`, `status`, and `reason`. |
| `startedAt` | string \| null | ISO 8601 timestamp of when the agent was first registered or created. Falls back to the agent's creation date when no registration record exists. |
| `subdomain` | string | Hostname of the agent's public URL |
| `url` | string | Full URL of the agent instance. Uses the persisted `openclawUrl` from the database when available, otherwise falls back to `https://agentbot-agent-{userId}-production.up.railway.app`. |
| `plan` | string | The user's subscription plan (for example, `solo`, `collective`, `label`). Defaults to `free` when no plan is set. |
| `openclawVersion` | string | OpenClaw runtime version. Prefers the version from `/healthz`, falls back to `/api/status`, then to the platform default. |
| `ffmpegAvailable` | boolean | `true` when the agent runtime reports that ffmpeg is available. Derived from the `/api/status` probe response. |
| `ffmpegVersion` | string \| null | ffmpeg version string reported by the agent runtime, or `null` when ffmpeg is not available or the probe failed. |
| `provisionedAt` | string \| null | ISO 8601 timestamp of when the instance was originally provisioned. Derived from the agent registration record or the agent's creation date. Returns `null` when neither is available. |
| `lastSeenAt` | string \| null | ISO 8601 timestamp of when the agent last checked in with the platform. Returns `null` when the agent has not reported since provisioning. |
| `gatewayProcessStatus` | string \| null | Status of the gateway process as reported by the registration record (for example, `active`). Returns `null` when no registration record exists. |
| `subscriptionStatus` | string \| null | The user's subscription status (for example, `active`, `canceled`, `past_due`). Returns `null` when no subscription is associated with the user. |

### Status values

| Status | Condition |
|--------|-----------|
| `running` | `/api/status` responds and reports the agent process as running |
| `healthy` | Both `/healthz` and `/readyz` respond successfully (when `/api/status` does not indicate a running state) |
| `starting` | `/healthz` responds but `/readyz` does not |
| `stopped` | `/api/status` responds but reports the agent process as stopped |
| `setup` | `/api/status` responds but reports the agent is not yet configured |
| `unknown` | `/api/status` responds with a non-standard state and legacy probes are inconclusive |
| `unreachable` | None of `/api/status`, `/healthz`, or `/readyz` respond successfully |

<Note>The shared runtime probe uses `/api/status` as the authoritative health signal. The legacy `/healthz` and `/readyz` endpoints may legitimately return `404` on some deployments. When `/api/status` returns `200`, the agent is considered reachable regardless of legacy probe results.</Note>

### Errors

| Code | Description |
|------|-------------|
| 403 | Forbidden — not authenticated (no valid session) |
| 404 | No instance found — the authenticated user does not have a deployed agent, or does not own the requested instance. The response message is: `No instance found. Please deploy first.` |

---

## Get instance stats

```http
GET /api/instance/:userId/stats
```

Requires session authentication and instance ownership. Returns runtime stats for a specific agent instance using the shared runtime probe, which checks `/healthz`, `/readyz`, and `/api/status`. Resource-level metrics (CPU, memory) are not yet exposed by the gateway and return placeholder values.

### Path parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `userId` | string | The instance identifier (matches the user's `openclawInstanceId`) |

### Response

```json
{
  "userId": "abc123",
  "status": "running",
  "health": "healthy",
  "cpu": "0%",
  "memory": "0MB",
  "uptime": "active",
  "messages": null,
  "errors": null,
  "openclawVersion": "2026.4.11",
  "statusReason": null,
  "probeChecks": [
    { "path": "/healthz", "ok": true, "status": 200, "reason": null },
    { "path": "/readyz", "ok": true, "status": 200, "reason": null },
    { "path": "/api/status", "ok": true, "status": 200, "reason": null }
  ],
  "telemetry": {
    "resourceMetricsAvailable": false,
    "lifecycleMetricsAvailable": false,
    "messageMetricsAvailable": false
  }
}
```

| Field | Type | Description |
|-------|------|-------------|
| `userId` | string | The instance identifier from the request path |
| `status` | string | Runtime status from the shared probe. Returns `running` when the probe reports `healthy`, otherwise returns the probe status directly (for example, `starting`, `stopped`, `setup`, `unknown`, `unreachable`). |
| `health` | string | `healthy` when the probe status is `running` or `healthy`, otherwise returns the probe status. |
| `cpu` | string | CPU usage. Currently returns `0%` as the gateway does not expose CPU metrics. |
| `memory` | string | Memory usage. Currently returns `0MB` as the gateway does not expose memory metrics. |
| `uptime` | string | Agent uptime from the probe. Falls back to `active` when the agent is running, or `unknown` when unreachable. |
| `messages` | number \| null | Message count. Currently returns `null` as message metrics are not yet available. |
| `errors` | number \| null | Error count. Currently returns `null` as error metrics are not yet available. |
| `openclawVersion` | string | OpenClaw runtime version from the shared probe. Falls back to the platform default. |
| `statusReason` | string \| null | Human-readable explanation when the status is not `running` or `healthy`. For example, `Legacy health probes unavailable; using /api/status`. |
| `probeChecks` | array | Per-probe results for `/healthz`, `/readyz`, and `/api/status`. Each entry includes `path`, `ok`, `status`, and `reason`. |
| `telemetry` | object | Indicates which metric categories are available |
| `telemetry.resourceMetricsAvailable` | boolean | `true` when CPU and memory metrics are available. Currently always `false`. |
| `telemetry.lifecycleMetricsAvailable` | boolean | `true` when lifecycle metrics (restart count, last exit) are available. Currently always `false`. |
| `telemetry.messageMetricsAvailable` | boolean | `true` when message-level metrics are available. Currently always `false`. |

<Note>The `telemetry` object is included to signal which metric categories the API supports. All three categories are currently `false` because the managed runtime does not yet expose restart counts, last-exit details, or per-message telemetry. These fields will transition to `true` as the underlying runtime adds support.</Note>

### Errors

| Code | Description |
|------|-------------|
| 401 | Unauthorized — no valid session |
| 403 | Forbidden — the authenticated user does not own the requested instance |

---

## Start instance

```http
POST /api/instance/:userId/start
```

Requires session authentication and instance ownership. Triggers a deploy of the user's agent service on Railway. The target service is resolved using the persisted `runtimeServiceId` when available. When managed runtime controls are disabled, this endpoint returns `503`.

### Path parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `userId` | string | The instance identifier |

### Response

```json
{
  "success": true,
  "status": "starting"
}
```

### Errors

| Code | Description |
|------|-------------|
| 401 | Unauthorized — no valid session |
| 403 | Forbidden — the authenticated user does not own the requested instance |
| 500 | Failed to start the service (for example, the Railway deploy mutation failed) |
| 503 | Managed runtime controls are disabled, or service/config resolution failed. See [503 error details](#503-error-details) for the full list of causes. |

---

## Stop instance

```http
POST /api/instance/:userId/stop
```

Requires session authentication and instance ownership. Stops (suspends) the user's agent service on Railway. The target service is resolved using the persisted `runtimeServiceId` when available. When managed runtime controls are disabled, this endpoint returns `503`.

### Path parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `userId` | string | The instance identifier |

### Response

```json
{
  "success": true,
  "status": "stopped"
}
```

### Errors

| Code | Description |
|------|-------------|
| 401 | Unauthorized — no valid session |
| 403 | Forbidden — the authenticated user does not own the requested instance |
| 500 | Failed to stop the service (for example, the Railway suspend mutation failed) |
| 503 | Managed runtime controls are disabled, or service/config resolution failed. See [503 error details](#503-error-details) for the full list of causes. |

---

## Restart instance

```http
POST /api/instance/:userId/restart
```

Requires session authentication and instance ownership. Restarts the user's agent gateway on Railway. The target service is resolved using the persisted `runtimeServiceId` when available. When managed runtime controls are disabled, this endpoint returns `503`.

### Path parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `userId` | string | The instance identifier |

### Response

```json
{
  "success": true,
  "status": "restarting"
}
```

### Errors

| Code | Description |
|------|-------------|
| 401 | Unauthorized — no valid session |
| 403 | Forbidden — the authenticated user does not own the requested instance |
| 500 | Failed to restart the service (for example, the Railway restart mutation failed) |
| 503 | Managed runtime controls are disabled, or service/config resolution failed. See [503 error details](#503-error-details) for the full list of causes. |

---

## Repair instance

```http
POST /api/instance/:userId/repair
```

Requires session authentication and instance ownership. Performs a full reconfigure of the agent service: rewrites all environment variables on Railway and restarts the container. Use this to fix broken gateway tokens, corrupted configuration, or stuck containers. The target service is resolved using the persisted `runtimeServiceId` when available. When managed runtime controls are disabled, this endpoint returns `503`.

The repair uses the user's per-user gateway token from the database. If no token exists, a new one is generated automatically.

### Path parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `userId` | string | The instance identifier |

### Response

```json
{
  "success": true,
  "status": "repaired"
}
```

### Errors

| Code | Description |
|------|-------------|
| 401 | Unauthorized — no valid session |
| 403 | Forbidden — the authenticated user does not own the requested instance |
| 500 | Failed to repair the service (for example, the environment variable update or Railway restart mutation failed) |
| 503 | Managed runtime controls are disabled, or service/config resolution failed. See [503 error details](#503-error-details) for the full list of causes. |

---

## Reset instance memory

```http
POST /api/instance/:userId/reset-memory
```

Requires session authentication and instance ownership. Wipes all stored agent memories from the database and restarts the container. The agent starts fresh as if newly provisioned. The target service is resolved using the persisted `runtimeServiceId` when available. When managed runtime controls are disabled, this endpoint returns `503`.

<Warning>This action permanently deletes all agent memory entries for the user. The workspace on Railway is ephemeral and resets on restart.</Warning>

### Path parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `userId` | string | The instance identifier |

### Response

```json
{
  "success": true,
  "status": "reset"
}
```

### Errors

| Code | Description |
|------|-------------|
| 401 | Unauthorized — no valid session |
| 403 | Forbidden — the authenticated user does not own the requested instance |
| 500 | Failed to reset agent memory or restart the service |
| 503 | Managed runtime controls are disabled, or service/config resolution failed. See [503 error details](#503-error-details) for the full list of causes. |

---

## Update instance image

```http
POST /api/instance/:userId/update
```

Requires session authentication and instance ownership. Updates the agent container to the platform's current default OpenClaw image and triggers a redeploy. The target service is resolved using the persisted `runtimeServiceId` when available. When managed runtime controls are disabled, this endpoint returns `503`.

### Path parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `userId` | string | The instance identifier |

### Response

```json
{
  "success": true,
  "status": "updating",
  "image": "ghcr.io/openclaw/openclaw:2026.4.11",
  "openclawVersion": "2026.4.11"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | `true` when the update was initiated |
| `status` | string | `updating` when the image change and redeploy were triggered |
| `image` | string | The Docker image the agent is being updated to |
| `openclawVersion` | string | The OpenClaw version corresponding to the new image |

### Errors

| Code | Description |
|------|-------------|
| 401 | Unauthorized — no valid session |
| 403 | Forbidden — the authenticated user does not own the requested instance |
| 500 | Failed to update the service |
| 503 | Managed runtime controls are disabled, or service/config resolution failed. See [503 error details](#503-error-details) for the full list of causes. |

---

## Get instance gateway token

```http
GET /api/instance/:userId/token
```

Requires session authentication and instance ownership. Returns the gateway token for the user's agent instance.

### Path parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `userId` | string | The instance identifier |

### Response

```json
{
  "token": "a1b2c3...64-char-hex"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `token` | string | The gateway token used to authenticate with the agent's OpenClaw instance |

### Errors

| Code | Description |
|------|-------------|
| 403 | Unauthorized — no valid session or the authenticated user does not own the requested instance |
| 503 | No gateway token is configured on the platform |

---

## 503 error details

All lifecycle endpoints (start, stop, restart, repair, reset-memory, and update) return `503` in two scenarios:

### Controls disabled

When managed runtime controls are disabled via the `ENABLE_OPENCLAW_CONTROLS` or `NEXT_PUBLIC_ENABLE_OPENCLAW_CONTROLS` environment variables, all lifecycle endpoints return:

```json
{
  "success": false,
  "error": "Managed runtime controls are temporarily disabled until the Railway control path is fully verified."
}
```

### Service or configuration resolution failure

When the platform cannot resolve the Railway service for the agent, lifecycle endpoints return `503` with a descriptive error message. This happens before the lifecycle action is attempted and indicates a configuration problem on the platform side, not an issue with the agent itself.

The platform resolves the Railway service using a two-tier strategy:

1. **Persisted service ID** — when the agent's configuration includes a `runtimeServiceId` (set during provisioning), the platform uses it directly. No Railway API call is made and no project-level listing is needed. This is the default for recently provisioned agents.
2. **Project-level discovery** — when no persisted service ID exists, the platform falls back to listing all services in the Railway project and matching by name candidates derived from the agent's URL and identifier. This fallback requires a Railway token with project-listing permissions.

When the persisted service ID is available, the only possible resolution errors are missing environment configuration (`RAILWAY_API_KEY`, `RAILWAY_ENVIRONMENT_ID`, or `RAILWAY_PROJECT_ID`). Ensure `RAILWAY_TOKEN_TYPE` is also set correctly — when using a project-scoped token, set it to `project` so the platform sends the key via the `Project-Access-Token` header instead of the default `Authorization: Bearer` header. The project-listing errors (`No managed service reference found` and `Managed Railway service not found`) only occur in the fallback path.

```json
{
  "success": false,
  "error": "RAILWAY_ENVIRONMENT_ID not configured"
}
```

Possible error messages include:

| Error message | Cause |
|---------------|-------|
| `RAILWAY_API_KEY not configured` | The platform's Railway API key is missing |
| `RAILWAY_ENVIRONMENT_ID not configured` | The Railway environment ID is not set |
| `RAILWAY_PROJECT_ID not configured` | The Railway project ID is not set |
| Authentication failures (403 from Railway) | `RAILWAY_TOKEN_TYPE` may be set incorrectly. Use `project` for project-scoped tokens or `account` (default) for personal tokens. |
| `No managed service reference found` | The platform could not determine the service name to look up (fallback path only — occurs when no persisted service ID exists and no agent URL or identifier is available) |
| `Managed Railway service not found for <candidates>` | No matching service was found in the Railway project for the expected service name(s) (fallback path only — occurs when no persisted service ID exists) |

<Note>Service resolution errors are distinct from operation errors. A `503` means the platform could not locate the agent's service to act on. A `500` means the service was found but the requested action (deploy, suspend, restart, or env var update) failed. Agents provisioned with the current platform version include a persisted service ID that bypasses project-level discovery entirely, making the last two error messages unlikely for new deployments.</Note>

---

## Get agent health

```http
GET /api/openclaw/maintenance
```

Requires session authentication. Returns liveness and readiness status for the user's agent container by probing the agent's `/healthz`, `/readyz`, and `/api/status` endpoints using the shared runtime probe.

<Note>This endpoint uses `GET /api/status` on the agent as the authoritative health signal. The legacy `/healthz` and `/readyz` probes are still checked for backward compatibility but may legitimately return `404` on some deployments. When `/api/status` returns `200`, the agent is considered reachable even if the legacy probes fail.</Note>

### Response

```json
{
  "instanceId": "abc123",
  "railwayUrl": "https://agentbot-agent-abc123-production.up.railway.app",
  "healthy": true,
  "ready": true,
  "version": "2026.4.11",
  "uptime": "2d 5h",
  "status": "running",
  "statusReason": null,
  "checks": [
    { "path": "/healthz", "ok": true, "status": 200, "reason": null },
    { "path": "/readyz", "ok": true, "status": 200, "reason": null },
    { "path": "/api/status", "ok": true, "status": 200, "reason": null }
  ]
}
```

| Field | Type | Description |
|-------|------|-------------|
| `instanceId` | string | The agent's instance identifier |
| `railwayUrl` | string | The URL used for health probes. This is the per-user `openclawUrl` from the database when available, otherwise falls back to the constructed Railway URL (`https://agentbot-agent-{instanceId}-production.up.railway.app`). Note that the per-user URL may be stale — for gateway connections, use the shared gateway URL instead (see [`GET /api/user/openclaw`](/api-reference/agents#get-user-openclaw-instance)). |
| `healthy` | boolean | `true` when the agent's `/healthz` endpoint responds successfully |
| `ready` | boolean | `true` when the agent's `/readyz` endpoint responds successfully |
| `version` | string \| null | Agent runtime version. Prefers the version from `/healthz`, falls back to `/api/status`, then to the platform default. |
| `uptime` | string \| null | Agent uptime from `/healthz` or `/api/status`, or `null` if unavailable |
| `status` | string | Computed status: `running`, `healthy`, `starting`, `stopped`, `setup`, `unknown`, or `unreachable`. See [status values](#status-values) below. |
| `statusReason` | string \| null | Human-readable explanation when the status is not `running` or `healthy`. For example, `Legacy health probes unavailable; using /api/status` when the agent is reachable via `/api/status` but `/healthz` and `/readyz` return `404`. |
| `checks` | array | Per-probe results for `/healthz`, `/readyz`, and `/api/status` |
| `checks[].path` | string | Probe path (for example, `/healthz`) |
| `checks[].ok` | boolean | `true` when the probe returned an HTTP 2xx response |
| `checks[].status` | number \| null | HTTP status code, or `null` if the request failed before receiving a response |
| `checks[].reason` | string \| null | Failure reason (for example, `HTTP 404`, `request failed`), or `null` on success |

### Status values

| Status | Condition |
|--------|-----------|
| `running` | `/api/status` responds successfully and reports the agent process as running. The agent is considered healthy even if `/healthz` and `/readyz` return `404`. |
| `healthy` | Both `/healthz` and `/readyz` respond successfully (when `/api/status` does not indicate a running state) |
| `starting` | `/healthz` responds but `/readyz` does not, or `/api/status` is reachable but the process is not fully ready |
| `stopped` | `/api/status` responds but reports the agent process as stopped |
| `setup` | `/api/status` responds but reports the agent is not yet configured (`configured: false`) |
| `unknown` | `/api/status` responds with a non-standard state and legacy probes are inconclusive |
| `unreachable` | None of `/api/status`, `/healthz`, or `/readyz` respond successfully |

### Response when no agent is deployed

When the authenticated user has no deployed agent:

```json
{
  "status": "no_agent",
  "healthy": false,
  "ready": false
}
```

<Note>The health check uses a 5-second timeout for `/healthz` and `/api/status`, and a 4-second timeout for `/readyz`. When `/api/status` is reachable but legacy probes fail, the `statusReason` field explains why the status was derived from `/api/status` alone.</Note>

### Errors

| Code | Description |
|------|-------------|
| 200 | Health status returned (check `status` field for actual health) |
| 401 | Unauthorized — missing or invalid session |

## Restart or reset agent

```http
POST /api/openclaw/maintenance
```

Requires session authentication. Triggers a restart or factory reset of the user's agent container. The `action` field in the request body determines the behavior.

This endpoint resolves the target Railway service using the persisted `runtimeServiceId` from the agent's configuration when available, falling back to project-level service discovery for older agents. See [service resolution](#service-or-configuration-resolution-failure) for details.

When managed runtime controls are disabled (via the `ENABLE_OPENCLAW_CONTROLS` or `NEXT_PUBLIC_ENABLE_OPENCLAW_CONTROLS` environment variables set to `false`), this endpoint returns `503` with the message: `Managed runtime controls are temporarily disabled until the Railway control path is fully verified.`

### Request body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `action` | string | No | The maintenance action to perform. One of `restart` or `factory-reset`. Defaults to `restart` when omitted. |

### Actions

#### `restart` (default)

Restarts the agent container. The agent automatically runs `openclaw doctor --fix` on startup, which performs health checks and applies any pending migrations.

**Request:**

```json
{
  "action": "restart"
}
```

**Response:**

```json
{
  "success": true,
  "message": "Agent restarting — doctor & migrations run on startup",
  "serviceId": "abc123-service-id",
  "instanceId": "abc123"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | `true` when the action was initiated |
| `message` | string | Confirmation message describing what happened |
| `serviceId` | string | Railway service identifier for the restarted agent |
| `instanceId` | string | The user's OpenClaw instance identifier |

#### `factory-reset`

Pins the agent to the known-good OpenClaw image (`v2026.4.11`), reconfigures environment variables, and restarts the container with `doctor --fix`. Use this when an agent is broken after updating to an incompatible version.

**Request:**

```json
{
  "action": "factory-reset"
}
```

**Response:**

```json
{
  "success": true,
  "message": "Factory reset complete — pinned to ghcr.io/openclaw/openclaw:2026.4.11. Agent restarting with doctor --fix.",
  "image": "ghcr.io/openclaw/openclaw:2026.4.11",
  "serviceId": "abc123-service-id"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | `true` when the action was initiated |
| `message` | string | Confirmation message describing what happened |
| `image` | string | The Docker image the agent was pinned to |
| `serviceId` | string | Railway service identifier for the reset agent |

### Error responses

When the user has no deployed agent:

```json
{
  "error": "No agent deployed"
}
```

When a restart or factory reset fails:

```json
{
  "error": "Description of the error"
}
```

| Code | Description |
|------|-------------|
| 200 | Action completed successfully |
| 401 | Unauthorized — missing or invalid session |
| 404 | No agent deployed for the authenticated user |
| 500 | Internal error during the restart or factory reset |
| 503 | Managed runtime controls are disabled, or the required Railway configuration (environment, project, or service) could not be resolved. When Railway configuration is the cause, the response body contains a descriptive error message. |

<Warning>Both `restart` and `factory-reset` cause a brief period of downtime while the container reinitializes. The `openclaw doctor --fix` process runs automatically during startup and may take additional time depending on the number of pending fixes or migrations.</Warning>

<Note>Factory reset pins the agent to the platform's configured default image. After a factory reset, you can update to a newer version later through the normal update flow.</Note>

## Ensure OpenClaw compatibility

```http
POST /api/openclaw/ensure-compatibility
```

Requires session authentication. Checks and migrates the authenticated user's agent setup for compatibility with OpenClaw 2026.4.11. This endpoint applies any necessary fixes automatically, including plugin config migration, agent pairing scope corrections, and token generation.

### Response

```json
{
  "compatible": true,
  "fixes": [
    "Migrated x_search plugin config",
    "Fixed agent pairing scope"
  ],
  "errors": [],
  "message": "Applied 2 compatibility fixes"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `compatible` | boolean | `true` when the user's setup is compatible with OpenClaw 2026.4.11 after applying any fixes |
| `fixes` | string[] | List of compatibility fixes that were applied. Empty when no fixes were needed. |
| `errors` | string[] | List of errors encountered during the compatibility check. Empty on success. |
| `message` | string | Summary message describing the result |

### Response when no fixes are needed

```json
{
  "compatible": true,
  "fixes": [],
  "errors": [],
  "message": "No fixes needed"
}
```

### Error response

When the compatibility check itself fails:

```json
{
  "compatible": false,
  "fixes": [],
  "errors": ["Description of the error"],
  "message": "Failed to ensure compatibility"
}
```

| Code | Description |
|------|-------------|
| 200 | Compatibility check completed (inspect `compatible` and `fixes` for details) |
| 401 | Unauthorized — missing or invalid session |
| 500 | Internal error during the compatibility check |

<Note>This endpoint is idempotent. Calling it multiple times applies only the fixes that have not already been applied. After all fixes are applied, subsequent calls return an empty `fixes` array.</Note>

---

## OpenClaw runtime version

```http
GET /api/openclaw/version
```

Returns the current OpenClaw runtime version and container image. Requires bearer token authentication (the `/api/openclaw` router applies the `authenticate` middleware to all non-proxy routes).

The backend normalizes the version before returning it. If the configured image uses a floating tag such as `latest`, the backend replaces it with the current managed baseline version (`2026.4.11`) so the response always contains a concrete release number.

### Response

```json
{
  "openclawVersion": "2026.4.11",
  "image": "ghcr.io/openclaw/openclaw:2026.4.11",
  "deployedAt": "2026-04-01T12:00:00Z"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `openclawVersion` | string | Current OpenClaw runtime version, derived from the configured image tag and normalized so floating tags like `latest` resolve to the managed baseline (for example, `2026.4.11`). |
| `image` | string | Docker image used for new agent containers. Defaults to the value of the `OPENCLAW_IMAGE` environment variable or `ghcr.io/openclaw/openclaw:2026.4.11`. |
| `deployedAt` | string | ISO 8601 timestamp of when the version info was served (returns the current server time, not the actual deployment time) |

## OpenClaw runtime version (web proxy)

```http
GET /api/openclaw-version
```

No authentication required. Proxies to the backend `GET /api/openclaw/version` endpoint using the internal API key. Returns the OpenClaw runtime version, container image, and deployment timestamp.

When the backend is unreachable or returns an error, the endpoint returns a fallback version derived from the configured image tag (for example, `2026.4.11`) and includes the default image.

### Version normalization

All version endpoints — the backend `GET /api/openclaw/version`, the web proxy `GET /api/openclaw-version`, and the web-layer `GET /api/openclaw/version` — normalize the `openclawVersion` value before returning it. If the configured image uses a floating tag like `latest`, the version is replaced with the current managed baseline version (currently `2026.4.11`).

This ensures every layer of the API returns a concrete release number rather than an opaque tag, so you can reliably compare the running version against known releases.

| Backend value | Returned value | Reason |
|---------------|----------------|--------|
| `"2026.4.11"` | `"2026.4.11"` | Concrete version returned as-is |
| `"latest"` | `"2026.4.11"` | Normalized to the managed baseline |
| `""` or missing | `"2026.4.11"` | Falls back to the managed baseline |

### Response

```json
{
  "openclawVersion": "2026.4.11",
  "image": "ghcr.io/openclaw/openclaw:2026.4.11",
  "deployedAt": "2026-04-01T12:00:00Z"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `openclawVersion` | string | OpenClaw runtime version from the backend, normalized to a concrete release number. When the backend returns `"latest"`, this field contains the current managed baseline version instead. Falls back to the managed baseline version when the backend is unavailable or returns an error. |
| `image` | string | Docker image identifier. When the backend is unavailable or does not return an image, defaults to the configured image (for example, `ghcr.io/openclaw/openclaw:2026.4.11`). |
| `deployedAt` | string \| undefined | ISO 8601 deployment timestamp. Only present when the backend responds successfully. |

<Note>This endpoint uses an 8-second request timeout when calling the backend. If the backend does not respond within that window, the fallback version is returned. Version normalization now applies at every layer: the backend `GET /api/openclaw/version`, the web proxy `GET /api/openclaw-version`, and the web-layer `GET /api/openclaw/version` all normalize floating tags to the managed baseline.</Note>

| Code | Description |
|------|-------------|
| 200 | Version returned (check `openclawVersion` for actual value — may be a fallback or normalized from `"latest"`) |

## Heal gateway token

```http
POST /api/support/heal-token
```

Requires session authentication. Auto-heals (regenerates) the gateway token for the authenticated user's OpenClaw agent. When the user has no existing token, the endpoint generates a new one automatically instead of only checking for an existing token.

The endpoint also checks gateway health and sends support alerts when token generation fails or the gateway is degraded.

### Response

```json
{
  "healed": true,
  "token": "a1b2c3...64-char-hex",
  "isNew": true,
  "health": {
    "name": "OpenClaw Gateway",
    "status": "ok",
    "detail": "ok"
  },
  "message": "New gateway token generated successfully"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `healed` | boolean | `true` when the token was successfully validated or regenerated |
| `token` | string | The user's gateway token (64-character hex string) |
| `isNew` | boolean | `true` when a new token was generated, `false` when an existing token was validated |
| `health` | object | Gateway health check result |
| `health.name` | string | Name of the checked service |
| `health.status` | string | Gateway status: `ok`, `degraded`, or `down` |
| `health.detail` | string | Additional detail about the health check result |
| `message` | string | Human-readable description of what happened |

### Response when token generation fails

```json
{
  "healed": false,
  "message": "Failed to generate gateway token. Support has been alerted.",
  "health": {
    "name": "OpenClaw Gateway",
    "status": "ok",
    "detail": "ok"
  }
}
```

### Error response

```json
{
  "healed": false,
  "message": "Internal error during token healing",
  "error": "Description of the error"
}
```

| Code | Description |
|------|-------------|
| 200 | Token healed or validated successfully |
| 401 | Unauthorized — missing or invalid session |
| 500 | Token generation failed or internal error |

<Note>This endpoint replaces the previous behavior where users had to manually refresh their pairing token. Tokens are now auto-generated when missing. The generated token is a 64-character hex string stored per-user in the database. Lifecycle operations such as [repair](/api-reference/agents#repair-agent) use the per-user token from the database when reconfiguring the agent runtime.</Note>

---

## List OpenClaw instances

```http
GET /api/openclaw/instances
```

Returns all running OpenClaw agent containers with their image, status, and metadata. Requires bearer token authentication.

### Response

```json
{
  "instances": [
    {
      "agentId": "agent_123",
      "name": "openclaw-agent_123",
      "image": "ghcr.io/openclaw/openclaw:2026.4.11",
      "status": "Up 2 days",
      "createdAt": "2026-04-01T00:00:00Z",
      "version": "2026.4.11",
      "metadata": {}
    }
  ],
  "count": 1
}
```

| Field | Type | Description |
|-------|------|-------------|
| `instances` | array | List of running OpenClaw containers |
| `instances[].agentId` | string | Agent identifier |
| `instances[].name` | string | Docker container name |
| `instances[].image` | string | Docker image the container is running |
| `instances[].status` | string | Docker status string |
| `instances[].createdAt` | string | Container creation timestamp |
| `instances[].version` | string | OpenClaw CLI version inside the container, or `unknown` |
| `instances[].metadata` | object \| null | Agent metadata from disk |
| `count` | number | Total number of instances |

### Errors

| Code | Description |
|------|-------------|
| 500 | Failed to list instances |

## Get instance container stats

```http
GET /api/openclaw/instances/:id/stats
```

Returns live resource usage for a specific OpenClaw container. Requires bearer token authentication.

### Path parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | string | Agent identifier |

### Response

```json
{
  "agentId": "agent_123",
  "cpu": "0.15%",
  "memory": "128MiB / 2GiB",
  "memoryPercent": "6.25%",
  "network": "1.2kB / 3.4kB",
  "blockIO": "0B / 0B",
  "pids": "12",
  "status": "running",
  "uptime": 86400000,
  "uptimeFormatted": "1d 0h",
  "timestamp": "2026-04-01T12:00:00Z"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `agentId` | string | Agent identifier |
| `cpu` | string | CPU usage percentage |
| `memory` | string | Memory usage (used / total) |
| `memoryPercent` | string | Memory usage percentage |
| `network` | string | Network I/O (in / out) |
| `blockIO` | string | Block I/O (read / write) |
| `pids` | string | Number of running processes |
| `status` | string | Container status |
| `uptime` | number | Uptime in milliseconds |
| `uptimeFormatted` | string | Human-readable uptime string |
| `timestamp` | string | ISO 8601 timestamp of the measurement |

### Errors

| Code | Description |
|------|-------------|
| 500 | Failed to get container stats |

## OpenClaw proxy

```
ALL /api/openclaw/proxy/:agentId/*
```

Transparent HTTP proxy to a running OpenClaw container. Forwards all HTTP methods to the container's internal address on port 18789. HTTP requests do not require authentication on the proxy itself — the OpenClaw instance's own token authentication handles access control.

<Warning>WebSocket upgrades through the proxy require bearer token authentication. The server validates the `Authorization: Bearer <token>` header before establishing the WebSocket connection. This is handled at the server level, separate from the HTTP proxy middleware.</Warning>

<Note>The proxy rewrites the request path by stripping the `/api/openclaw/proxy/:agentId` prefix before forwarding.</Note>

### Path parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `agentId` | string | Agent identifier (alphanumeric, hyphens, and underscores only) |

### Errors

| Code | Description |
|------|-------------|
| 502 | OpenClaw instance unreachable |
