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

# Events

> Every event Runcrate sends, when it fires, the fields in its data and an example of each.

Every event arrives in the same [envelope](/docs/webhooks/overview#the-envelope). This page lists each `type`, when it is sent and what its `data` holds. The examples are the exact JSON a signed webhook endpoint receives. Slack and Discord get the same event drawn as a message (see [Slack](/docs/webhooks/slack) and [Discord](/docs/webhooks/discord)).

## Versioning

Each event carries `api_version`, a date naming the format it follows. The examples below show the current one. Within one version:

* `data` is open. New keys can appear in any event at any time, so ignore keys you do not use and never reject an event for having one you do not know.
* An existing key never changes its meaning or its type.
* New event types can be added. An endpoint set to **All events** receives them; one set to **Choose events** only gets what it picked.

A change that would break a receiver comes with a new `api_version`.

## Choosing events

In **Settings > Webhooks**, each endpoint has an **Events** setting: **All events**, or **Choose events** to pick from the groups below. `webhook.test` is the exception: it goes to the endpoint you test, whatever it subscribes to.

Fields are snake\_case. Money is in USD as a number (`412.37`), times are ISO 8601 in UTC, and ids are UUIDs. In instance events, `deployment_id` is the instance `id` you get from the API, CLI and MCP tools.

| Event | Group | What it means |
| - | - | - |
| `budget.threshold_reached` | Budgets | An environment's month to date reached 80% of its monthly budget. |
| `budget.reached` | Budgets | An environment spent its full monthly budget: launches are refused, instances stop and its API keys are declined. |
| `instance.ready` | Instances | An instance or crate is running and reachable over SSH and its ports. |
| `instance.launch_failed` | Instances | A launch did not start. Nothing was charged for it. |
| `instance.runtime_ending` | Instances | An instance is 10 minutes from its max runtime. |
| `instance.auto_stopped` | Instances | Runcrate stopped an instance: max runtime, idle stop, a budget or a low balance. |
| `instance.stopped` | Instances | An instance was terminated for any reason. stop\_reason says why. |
| `job.queued` | Jobs | A job is waiting: no capacity for the GPUs it asked for yet, or the budget. Sent once per attempt. |
| `job.started` | Jobs | A job's command started running on its machine. |
| `job.finished` | Jobs | A job ended: succeeded, failed, timed out or cancelled. |
| `schedule.failed` | Schedules | A scheduled launch did not start. |
| `schedule.paused` | Schedules | A schedule was turned off: 5 scheduled runs in a row failed, or who it runs as lost access. |
| `balance.low` | Billing | The balance is running low. When stops\_at is set, running instances stop then unless money is added. |
| `balance.depleted` | Billing | The balance ran out and running instances were stopped. |
| `credits.added` | Billing | A payment or top-up was added to the workspace balance. |
| `storage.suspended` | Storage | A storage volume was suspended because its storage could not be paid for. |
| `storage.reactivated` | Storage | A suspended storage volume is available again. |
| `webhook.test` | Webhooks | Sent by Send a test event. Never sent on its own. |
| `webhook.disabled` | Webhooks | An endpoint kept failing (3 days with no success and at least 10 failed attempts, or 20 events in a row given up on), so Runcrate turned it off. |

## Budgets

### budget.threshold\_reached

An environment's month to date reached 80% of its monthly budget.

**When:** Once per environment, month and budget amount, when the month to date first reaches 80% of the budget. Raising the budget can send it again at the new amount. Owners and admins also get an email.

| Field | Type | Meaning |
| - | - | - |
| `environment_id` | string (UUID) | The environment the budget belongs to. |
| `environment_name` | string | The environment's name. |
| `monthly_budget_usd` | number | The environment's monthly budget, in USD. |
| `month` | string | The UTC month the spend counts toward, as `YYYY-MM`. |
| `month_to_date_usd` | number | What the environment has spent this month (GPU instances and inference), in USD. |
| `percent` | integer | How much of the budget is spent, in percent: `80`. |

```json Example theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "id": "6f1d3c2a-8a4e-4b7f-9a51-2f0c1e7d9000",
  "type": "budget.threshold_reached",
  "api_version": "2026-10-20",
  "created_at": "2026-10-20T14:02:11.000Z",
  "workspace_id": "a3c9e2f0-1b7d-4e55-8f20-6d4b9c1a7e33",
  "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
  "data": {
    "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
    "environment_name": "production",
    "monthly_budget_usd": 500,
    "month": "2026-10",
    "month_to_date_usd": 402.17,
    "percent": 80
  }
}
```

### budget.reached

An environment spent its full monthly budget: launches are refused, instances stop and its API keys are declined.

**When:** Once per environment, month and budget amount, when the month to date reaches the budget. From then on launches in the environment fail with HTTP 402, its running instances are terminated (each sends `instance.auto_stopped` with `stop_reason` `budget`) and its API keys are declined, until the next UTC month or until the budget is raised. Owners and admins also get an email.

| Field | Type | Meaning |
| - | - | - |
| `environment_id` | string (UUID) | The environment the budget belongs to. |
| `environment_name` | string | The environment's name. |
| `monthly_budget_usd` | number | The environment's monthly budget, in USD. |
| `month` | string | The UTC month the spend counts toward, as `YYYY-MM`. |
| `month_to_date_usd` | number | What the environment has spent this month (GPU instances and inference), in USD. |
| `percent` | integer | How much of the budget is spent, in percent: `100`. |

```json Example theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "id": "6f1d3c2a-8a4e-4b7f-9a51-2f0c1e7d9001",
  "type": "budget.reached",
  "api_version": "2026-10-20",
  "created_at": "2026-10-20T14:02:11.000Z",
  "workspace_id": "a3c9e2f0-1b7d-4e55-8f20-6d4b9c1a7e33",
  "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
  "data": {
    "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
    "environment_name": "production",
    "monthly_budget_usd": 500,
    "month": "2026-10",
    "month_to_date_usd": 500.42,
    "percent": 100
  }
}
```

## Instances

### instance.ready

An instance or crate is running and reachable over SSH and its ports.

**When:** Once per launch, when the instance or crate reaches `running`.

| Field | Type | Meaning |
| - | - | - |
| `deployment_id` | string (UUID) | The instance's `id`, the same one the API, CLI and MCP tools use. |
| `deployment_name` | string or `null` | The instance's name. |
| `environment_id` | string (UUID) | The environment the instance runs in. |
| `kind` | string | `instance`, or `crate` for a crate launch. |
| `gpu_type` | string or `null` | The GPU model, for example `H100`. |
| `gpu_count` | integer or `null` | How many GPUs it has. |
| `region` | string or `null` | The region it runs in. |
| `ip` | string or `null` | Its public IP address. `null` when it has none yet. |
| `ssh_command` | string or `null` | The command that connects to it over SSH. `null` when it has no IP yet. |
| `hourly_rate_usd` | number | What it costs per hour, in USD. |
| `ready_at` | string (ISO 8601) | When it became ready. |

```json Example theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "id": "6f1d3c2a-8a4e-4b7f-9a51-2f0c1e7d9002",
  "type": "instance.ready",
  "api_version": "2026-10-20",
  "created_at": "2026-10-20T14:02:11.000Z",
  "workspace_id": "a3c9e2f0-1b7d-4e55-8f20-6d4b9c1a7e33",
  "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
  "data": {
    "deployment_id": "7d2e9c41-5b3a-4f60-8e17-c4a0b9d2f655",
    "deployment_name": "train-llama-8b",
    "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
    "kind": "instance",
    "gpu_type": "H100",
    "gpu_count": 1,
    "region": "us-east",
    "ip": "203.0.113.24",
    "ssh_command": "ssh root@203.0.113.24",
    "hourly_rate_usd": 2.49,
    "ready_at": "2026-10-20T14:02:11.000Z"
  }
}
```

### instance.launch\_failed

A launch did not start. Nothing was charged for it.

**When:** When a launch ends before the instance ever ran. A launch that never ran is not billed.

| Field | Type | Meaning |
| - | - | - |
| `deployment_id` | string (UUID) | The instance's `id`, the same one the API, CLI and MCP tools use. |
| `deployment_name` | string or `null` | The instance's name. |
| `environment_id` | string (UUID) | The environment the instance runs in. |
| `failure_reason` | string | A stable code for the failure: `create_failed`, `machine_error` or `provision_timeout`. Show `message` to people, not this code. |
| `message` | string | One sentence about what happened, fit to show to people. |
| `failed_at` | string (ISO 8601) | When the launch failed. |

```json Example theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "id": "6f1d3c2a-8a4e-4b7f-9a51-2f0c1e7d9003",
  "type": "instance.launch_failed",
  "api_version": "2026-10-20",
  "created_at": "2026-10-20T14:02:11.000Z",
  "workspace_id": "a3c9e2f0-1b7d-4e55-8f20-6d4b9c1a7e33",
  "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
  "data": {
    "deployment_id": "7d2e9c41-5b3a-4f60-8e17-c4a0b9d2f655",
    "deployment_name": "train-llama-8b",
    "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
    "failure_reason": "provision_timeout",
    "message": "The machine took too long to start, so it was cancelled. You were not charged.",
    "failed_at": "2026-10-20T14:02:11.000Z"
  }
}
```

### instance.runtime\_ending

An instance is 10 minutes from its max runtime.

**When:** Ten minutes before an instance reaches its max runtime. Changing the max runtime sends a fresh one ten minutes before the new stop time. Owners and admins also get an email.

| Field | Type | Meaning |
| - | - | - |
| `deployment_id` | string (UUID) | The instance's `id`, the same one the API, CLI and MCP tools use. |
| `deployment_name` | string | The instance's name. |
| `environment_id` | string (UUID) | The environment the instance runs in. |
| `auto_stop_at` | string (ISO 8601) | When the instance will be terminated. |
| `max_runtime_minutes` | integer | The max runtime set on the instance, in minutes. |

```json Example theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "id": "6f1d3c2a-8a4e-4b7f-9a51-2f0c1e7d9004",
  "type": "instance.runtime_ending",
  "api_version": "2026-10-20",
  "created_at": "2026-10-20T14:02:11.000Z",
  "workspace_id": "a3c9e2f0-1b7d-4e55-8f20-6d4b9c1a7e33",
  "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
  "data": {
    "deployment_id": "7d2e9c41-5b3a-4f60-8e17-c4a0b9d2f655",
    "deployment_name": "train-llama-8b",
    "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
    "auto_stop_at": "2026-10-20T14:12:11.000Z",
    "max_runtime_minutes": 240
  }
}
```

### instance.auto\_stopped

Runcrate stopped an instance: max runtime, idle stop, a budget or a low balance.

**When:** When Runcrate terminates an instance on its own: `stop_reason` is `max_runtime`, `idle`, `budget` or `low_balance`. The same termination also sends `instance.stopped`. Owners and admins also get an email.

| Field | Type | Meaning |
| - | - | - |
| `deployment_id` | string (UUID) | The instance's `id`, the same one the API, CLI and MCP tools use. |
| `deployment_name` | string | The instance's name. |
| `environment_id` | string (UUID) | The environment the instance runs in. |
| `stop_reason` | string | Why it was stopped: `max_runtime`, `idle`, `budget` or `low_balance`. |
| `stopped_at` | string (ISO 8601) | When it was stopped. |
| `max_runtime_minutes` | integer or `null` | The instance's max runtime in minutes, or `null` when it had none. |
| `idle_shutdown_minutes` | integer or `null` | The idle stop in minutes when idle stop stopped the instance, otherwise `null`. |

```json Example theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "id": "6f1d3c2a-8a4e-4b7f-9a51-2f0c1e7d9005",
  "type": "instance.auto_stopped",
  "api_version": "2026-10-20",
  "created_at": "2026-10-20T14:02:11.000Z",
  "workspace_id": "a3c9e2f0-1b7d-4e55-8f20-6d4b9c1a7e33",
  "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
  "data": {
    "deployment_id": "7d2e9c41-5b3a-4f60-8e17-c4a0b9d2f655",
    "deployment_name": "train-llama-8b",
    "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
    "stop_reason": "max_runtime",
    "stopped_at": "2026-10-20T14:02:11.000Z",
    "max_runtime_minutes": 240,
    "idle_shutdown_minutes": null
  }
}
```

### instance.stopped

An instance was terminated for any reason. stop\_reason says why.

**When:** Every time an instance is terminated, whoever or whatever stopped it.

| Field | Type | Meaning |
| - | - | - |
| `deployment_id` | string (UUID) | The instance's `id`, the same one the API, CLI and MCP tools use. |
| `deployment_name` | string | The instance's name. |
| `environment_id` | string (UUID) | The environment the instance runs in. |
| `stop_reason` | string | Why it stopped: `user`, `max_runtime`, `idle`, `budget`, `low_balance`, `failed`, `system` or `job_finished`. |
| `stopped_at` | string (ISO 8601) | When it stopped. |

```json Example theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "id": "6f1d3c2a-8a4e-4b7f-9a51-2f0c1e7d9006",
  "type": "instance.stopped",
  "api_version": "2026-10-20",
  "created_at": "2026-10-20T14:02:11.000Z",
  "workspace_id": "a3c9e2f0-1b7d-4e55-8f20-6d4b9c1a7e33",
  "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
  "data": {
    "deployment_id": "7d2e9c41-5b3a-4f60-8e17-c4a0b9d2f655",
    "deployment_name": "train-llama-8b",
    "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
    "stop_reason": "user",
    "stopped_at": "2026-10-20T14:02:11.000Z"
  }
}
```

## Jobs

Every job event also carries `attempt` (1-based), `attempts_count`, `group_id` (`null` for a single job) and `tags`. A job's own `webhook_url` gets its `job.started` and `job.finished` too, signed the same way (see [Jobs: idempotency and callbacks](/docs/jobs/idempotency-and-callbacks)).

### job.queued

A job is waiting: no GPU it asked for has capacity yet, or its environment's budget is spent.

**When:** Once per attempt, when the job starts waiting. It is tried again until its `capacity_wait_minutes` are over, then it fails.

| Field | Type | Meaning |
| - | - | - |
| `job_id` | string (UUID) | The job's `id`. |
| `job_name` | string | The job's name. |
| `environment_id` | string (UUID) | The environment the job runs in. |
| `reason` | string | `no_capacity` or `budget`. |
| `gpu_candidates` | array | The GPUs it asked for, in order, as `{ "gpu_type", "gpu_count" }`. |
| `next_attempt_at` | string (ISO 8601) | When it is tried next. |
| `wait_until` | string (ISO 8601) | When it stops waiting and fails. |

```json Example theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "id": "6f1d3c2a-8a4e-4b7f-9a51-2f0c1e7d9006",
  "type": "job.queued",
  "api_version": "2026-10-20",
  "created_at": "2026-10-20T14:02:11.000Z",
  "workspace_id": "a3c9e2f0-1b7d-4e55-8f20-6d4b9c1a7e33",
  "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
  "data": {
    "job_id": "0b6f3e28-9c1d-4a75-b2e4-81f7c3d5a960",
    "job_name": "nightly-eval",
    "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
    "reason": "no_capacity",
    "gpu_candidates": [{ "gpu_type": "H100", "gpu_count": 1 }, { "gpu_type": "A100-80GB", "gpu_count": 2 }],
    "next_attempt_at": "2026-10-20T14:03:11.000Z",
    "wait_until": "2026-10-20T14:32:11.000Z",
    "attempt": 1,
    "attempts_count": 1,
    "group_id": null,
    "tags": { "team": "ml" }
  }
}
```

### job.started

A job's command started running on its machine.

**When:** When a job's machine is ready and its command starts: once per attempt.

| Field | Type | Meaning |
| - | - | - |
| `job_id` | string (UUID) | The job's `id`. |
| `job_name` | string | The job's name. |
| `environment_id` | string (UUID) | The environment the job runs in. |
| `gpu_type` | string | The GPU model, for example `A100`. |
| `gpu_count` | integer | How many GPUs the job has. |
| `max_runtime_minutes` | integer | The job's maximum runtime, in minutes. |
| `started_at` | string (ISO 8601) | When the command started. |

```json Example theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "id": "6f1d3c2a-8a4e-4b7f-9a51-2f0c1e7d9007",
  "type": "job.started",
  "api_version": "2026-10-20",
  "created_at": "2026-10-20T14:02:11.000Z",
  "workspace_id": "a3c9e2f0-1b7d-4e55-8f20-6d4b9c1a7e33",
  "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
  "data": {
    "job_id": "0b6f3e28-9c1d-4a75-b2e4-81f7c3d5a960",
    "job_name": "nightly-eval",
    "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
    "gpu_type": "A100",
    "gpu_count": 1,
    "max_runtime_minutes": 120,
    "started_at": "2026-10-20T14:02:11.000Z",
    "attempt": 1,
    "attempts_count": 1,
    "group_id": null,
    "tags": { "team": "ml" }
  }
}
```

### job.finished

A job ended: succeeded, failed, timed out or cancelled.

**When:** Once per job, when it reaches a final status (a retried attempt does not end the job). A job whose machine never started gets one too, with status `failed` or `cancelled` and `null` for what never happened.

| Field | Type | Meaning |
| - | - | - |
| `job_id` | string (UUID) | The job's `id`. |
| `job_name` | string | The job's name. |
| `environment_id` | string (UUID) | The environment the job runs in. |
| `status` | string | `succeeded`, `failed`, `timed_out` or `cancelled`. |
| `exit_code` | integer or `null` | The command's exit code. `null` when the command never reported one. |
| `error` | string or `null` | One sentence about why the job did not succeed. `null` when it succeeded. |
| `started_at` | string (ISO 8601) or `null` | When the command started. `null` when it never started. |
| `ended_at` | string (ISO 8601) | When the job ended. |
| `duration_seconds` | integer or `null` | How long the command ran, in seconds. `null` when it never started. |
| `cost_usd` | number or `null` | What the job's machines cost, every attempt added up, in USD. `null` when no machine ran. |
| `logs_url` | string | The API URL of the job's logs. Call it with an API key. |

```json Example theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "id": "6f1d3c2a-8a4e-4b7f-9a51-2f0c1e7d9008",
  "type": "job.finished",
  "api_version": "2026-10-20",
  "created_at": "2026-10-20T14:02:11.000Z",
  "workspace_id": "a3c9e2f0-1b7d-4e55-8f20-6d4b9c1a7e33",
  "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
  "data": {
    "job_id": "0b6f3e28-9c1d-4a75-b2e4-81f7c3d5a960",
    "job_name": "nightly-eval",
    "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
    "status": "succeeded",
    "exit_code": 0,
    "error": null,
    "started_at": "2026-10-20T13:15:11.000Z",
    "ended_at": "2026-10-20T14:02:11.000Z",
    "duration_seconds": 2820,
    "cost_usd": 1.37,
    "logs_url": "https://www.runcrate.ai/api/v1/jobs/0b6f3e28-9c1d-4a75-b2e4-81f7c3d5a960/logs",
    "attempt": 1,
    "attempts_count": 1,
    "group_id": null,
    "tags": { "team": "ml" }
  }
}
```

## Schedules

### schedule.failed

A scheduled launch did not start.

**When:** When a scheduled run was due and did not start. Nothing is charged for it. When the failure is the 5th scheduled run in a row, the schedule is paused and `schedule.paused` is sent instead. See [schedules](/docs/schedules/overview). Owners and admins also get an email.

| Field | Type | Meaning |
| - | - | - |
| `schedule_id` | string (UUID) | The schedule's `id`. |
| `schedule_name` | string | The schedule's name. |
| `kind` | string | What the schedule launches: `instance` or `job`. |
| `error` | string | One sentence about why the launch did not start. |
| `scheduled_for` | string (ISO 8601) | When the run was due. |
| `recurring` | boolean | Whether the schedule repeats. A one-off schedule does not run again. |

```json Example theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "id": "6f1d3c2a-8a4e-4b7f-9a51-2f0c1e7d9009",
  "type": "schedule.failed",
  "api_version": "2026-10-20",
  "created_at": "2026-10-20T14:02:11.000Z",
  "workspace_id": "a3c9e2f0-1b7d-4e55-8f20-6d4b9c1a7e33",
  "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
  "data": {
    "schedule_id": "5e8a1c73-2f4b-4d09-9a6e-3b7c0d2f1e48",
    "schedule_name": "weekday-dev-box",
    "kind": "instance",
    "error": "No machine of this type was available. Nothing was charged.",
    "scheduled_for": "2026-10-20T14:02:11.000Z",
    "recurring": true
  }
}
```

### schedule.paused

A schedule was turned off: 5 scheduled runs in a row failed, or who it runs as lost access.

**When:** When Runcrate turns a schedule off: 5 scheduled runs in a row failed, the person it runs as left the workspace, or the API key or agent sign-in behind it was revoked or ended. It does not run again until someone resumes it. Owners and admins also get an email.

| Field | Type | Meaning |
| - | - | - |
| `schedule_id` | string (UUID) | The schedule's `id`. |
| `schedule_name` | string | The schedule's name. |
| `kind` | string | What the schedule launches: `instance` or `job`. |
| `reason` | string | `failing` (5 scheduled runs in a row failed), `no_access` (the person it runs as left the workspace) or `credential_revoked` (its API key or agent sign-in was revoked or ended). |
| `consecutive_failures` | integer or `null` | How many scheduled runs in a row failed. `null` unless `reason` is `failing`. |
| `error` | string | The last failure, as one sentence. |
| `paused_at` | string (ISO 8601) | When the schedule was paused. |

```json Example theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "id": "6f1d3c2a-8a4e-4b7f-9a51-2f0c1e7d9010",
  "type": "schedule.paused",
  "api_version": "2026-10-20",
  "created_at": "2026-10-20T14:02:11.000Z",
  "workspace_id": "a3c9e2f0-1b7d-4e55-8f20-6d4b9c1a7e33",
  "environment_id": "e81b4f6a-2c3d-4a9e-b710-5f8c2d1e9a04",
  "data": {
    "schedule_id": "5e8a1c73-2f4b-4d09-9a6e-3b7c0d2f1e48",
    "schedule_name": "weekday-dev-box",
    "kind": "instance",
    "reason": "failing",
    "consecutive_failures": 5,
    "error": "No machine of this type was available. Nothing was charged.",
    "paused_at": "2026-10-20T14:02:11.000Z"
  }
}
```

## Billing

### balance.low

The balance is running low. When stops\_at is set, running instances stop then unless money is added.

**When:** When the workspace balance falls under $10, and again when it falls under $1 and a grace period starts: running instances stop at `stops_at` unless funds arrive. Add funds or turn on [auto-recharge](/docs/billing/auto-recharge) to keep instances running. Workspace-wide: the envelope's `environment_id` is `null`.

| Field | Type | Meaning |
| - | - | - |
| `balance_usd` | number | The workspace balance, in USD. |
| `stops_at` | string (ISO 8601) or `null` | When running instances will stop unless funds are added. `null` for the under-\$10 warning. |

```json Example theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "id": "6f1d3c2a-8a4e-4b7f-9a51-2f0c1e7d9011",
  "type": "balance.low",
  "api_version": "2026-10-20",
  "created_at": "2026-10-20T14:02:11.000Z",
  "workspace_id": "a3c9e2f0-1b7d-4e55-8f20-6d4b9c1a7e33",
  "environment_id": null,
  "data": {
    "balance_usd": 4.12,
    "stops_at": null
  }
}
```

### balance.depleted

The balance ran out and running instances were stopped.

**When:** When the workspace balance runs out. Running instances are terminated, each with `stop_reason` `low_balance`. Workspace-wide: the envelope's `environment_id` is `null`.

| Field | Type | Meaning |
| - | - | - |
| `balance_usd` | number | The workspace balance, in USD. It can be below zero. |
| `instances_stopped` | integer | How many running instances were stopped. |

```json Example theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "id": "6f1d3c2a-8a4e-4b7f-9a51-2f0c1e7d9012",
  "type": "balance.depleted",
  "api_version": "2026-10-20",
  "created_at": "2026-10-20T14:02:11.000Z",
  "workspace_id": "a3c9e2f0-1b7d-4e55-8f20-6d4b9c1a7e33",
  "environment_id": null,
  "data": {
    "balance_usd": 0,
    "instances_stopped": 2
  }
}
```

### credits.added

A payment or top-up was added to the workspace balance.

**When:** When money is added to the workspace balance: a top-up, an auto-recharge or an adjustment. Workspace-wide: the envelope's `environment_id` is `null`.

| Field | Type | Meaning |
| - | - | - |
| `amount_usd` | number | The amount added, in USD. |
| `balance_usd` | number | The workspace balance after it was added, in USD. |
| `source` | string | `top_up`, `auto_recharge` or `adjustment`. |
| `transaction_id` | string (UUID) | The `id` of the transaction on the billing page. |

```json Example theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "id": "6f1d3c2a-8a4e-4b7f-9a51-2f0c1e7d9013",
  "type": "credits.added",
  "api_version": "2026-10-20",
  "created_at": "2026-10-20T14:02:11.000Z",
  "workspace_id": "a3c9e2f0-1b7d-4e55-8f20-6d4b9c1a7e33",
  "environment_id": null,
  "data": {
    "amount_usd": 100,
    "balance_usd": 104.12,
    "source": "top_up",
    "transaction_id": "b7e2c9a1-3d5f-4e08-9c6b-1a2f4d8e0c37"
  }
}
```

## Storage

### storage.suspended

A storage volume was suspended because its storage could not be paid for.

**When:** When a storage volume is suspended because the workspace could not pay for it. `storage.reactivated` follows once it is paid for. Workspace-wide: the envelope's `environment_id` is `null`.

| Field | Type | Meaning |
| - | - | - |
| `storage_id` | string (UUID) | The volume's `id`. |
| `storage_name` | string | The volume's name. |
| `size_gb` | integer | The volume's size, in GB. |
| `reason` | string | Why it was suspended: `unpaid`. |
| `suspended_at` | string (ISO 8601) | When it was suspended. |

```json Example theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "id": "6f1d3c2a-8a4e-4b7f-9a51-2f0c1e7d9014",
  "type": "storage.suspended",
  "api_version": "2026-10-20",
  "created_at": "2026-10-20T14:02:11.000Z",
  "workspace_id": "a3c9e2f0-1b7d-4e55-8f20-6d4b9c1a7e33",
  "environment_id": null,
  "data": {
    "storage_id": "9c4d7b12-6e0a-4f38-a5c1-2d8e7f3b0a69",
    "storage_name": "datasets",
    "size_gb": 500,
    "reason": "unpaid",
    "suspended_at": "2026-10-20T14:02:11.000Z"
  }
}
```

### storage.reactivated

A suspended storage volume is available again.

**When:** When a suspended volume is paid for and can be attached again. Workspace-wide: the envelope's `environment_id` is `null`.

| Field | Type | Meaning |
| - | - | - |
| `storage_id` | string (UUID) | The volume's `id`. |
| `storage_name` | string | The volume's name. |
| `size_gb` | integer | The volume's size, in GB. |
| `reactivated_at` | string (ISO 8601) | When it became available again. |

```json Example theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "id": "6f1d3c2a-8a4e-4b7f-9a51-2f0c1e7d9015",
  "type": "storage.reactivated",
  "api_version": "2026-10-20",
  "created_at": "2026-10-20T14:02:11.000Z",
  "workspace_id": "a3c9e2f0-1b7d-4e55-8f20-6d4b9c1a7e33",
  "environment_id": null,
  "data": {
    "storage_id": "9c4d7b12-6e0a-4f38-a5c1-2d8e7f3b0a69",
    "storage_name": "datasets",
    "size_gb": 500,
    "reactivated_at": "2026-10-20T14:02:11.000Z"
  }
}
```

## Webhooks

### webhook.test

Sent by Send a test event. Never sent on its own.

**When:** Only when someone presses **Send a test event**, and only to that one endpoint, whatever events it subscribes to. You cannot subscribe to it. Each person can send 10 tests per 10 minutes. Workspace-wide: the envelope's `environment_id` is `null`.

| Field | Type | Meaning |
| - | - | - |
| `test` | boolean | Always `true`. |
| `message` | string | A sentence saying this is a test. |
| `webhook_id` | string (UUID) | The endpoint the test was sent to. |

```json Example theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "id": "6f1d3c2a-8a4e-4b7f-9a51-2f0c1e7d9016",
  "type": "webhook.test",
  "api_version": "2026-10-20",
  "created_at": "2026-10-20T14:02:11.000Z",
  "workspace_id": "a3c9e2f0-1b7d-4e55-8f20-6d4b9c1a7e33",
  "environment_id": null,
  "data": {
    "test": true,
    "message": "A test event from Runcrate. Nothing happened in your workspace.",
    "webhook_id": "d1f0a7c3-4e2b-4c86-9b5d-7a3e1f0c8b24"
  }
}
```

### webhook.disabled

An endpoint kept failing (3 days with no success and at least 10 failed attempts, or 20 events in a row given up on), so Runcrate turned it off.

**When:** When Runcrate turns an endpoint off (see [automatic turn-off](/docs/webhooks/retries#automatic-turn-off)). It goes to your other endpoints that subscribe to it, never to the one turned off. Owners and admins also get an email. Workspace-wide: the envelope's `environment_id` is `null`.

| Field | Type | Meaning |
| - | - | - |
| `webhook_id` | string (UUID) | The endpoint that was turned off. |
| `channel` | string | `webhook`, `slack` or `discord`. |
| `url_display` | string | The endpoint's host and the last four characters of its URL. The full URL is never sent. |
| `reason` | string | Why it was turned off: `failing`. |
| `failing_since` | string (ISO 8601) | When the run of failures began. |
| `last_error` | string | The last failure, as one sentence. |

```json Example theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "id": "6f1d3c2a-8a4e-4b7f-9a51-2f0c1e7d9017",
  "type": "webhook.disabled",
  "api_version": "2026-10-20",
  "created_at": "2026-10-20T14:02:11.000Z",
  "workspace_id": "a3c9e2f0-1b7d-4e55-8f20-6d4b9c1a7e33",
  "environment_id": null,
  "data": {
    "webhook_id": "d1f0a7c3-4e2b-4c86-9b5d-7a3e1f0c8b24",
    "channel": "webhook",
    "url_display": "hooks.example.com/…f3a9",
    "reason": "failing",
    "failing_since": "2026-10-17T14:02:11.000Z",
    "last_error": "The server behind this URL had an error (it answered 502)."
  }
}
```

## Related

> **[Verifying signatures](/docs/webhooks/verifying-signatures)**
>
> Check that an event came from Runcrate.

> **[Retries and the delivery log](/docs/webhooks/retries)**
>
> What happens when your endpoint does not answer.

> **[Spend guardrails](/docs/instances/guardrails)**
>
> Max runtime, idle stop and monthly budgets, which send the budget and auto stop events.

> **[Best practices](/docs/webhooks/best-practices)**
>
> Dedupe, ordering and keeping the secret safe.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.