# Sinks

A sink is the action a job performs when it fires. cronpad has two: **webhooks**, which send an HTTP request to a URL you choose, and **commands**, which run a program on the cronpad server (self-hosted only). Each attempt's result is classified as success, retryable failure or fatal failure, which drives [retries](../retries-and-failures/retries.md) and [alerts](../alerts/index.md).

| Sink | Available on | Page |
|---|---|---|
| Webhook | Cloud and self-hosted | [Webhooks](webhooks.md) |
| Command | Self-hosted only (disabled in `mode = "hosted"`) | [Commands](commands.md) |

## Outcomes

| Outcome | Meaning | What happens next |
|---|---|---|
| `success` | The sink reported success (2xx, or exit code 0 by default) | Run `succeeded` |
| `retryable` | A failure that might succeed later (5xx, 429, network error) | Retry with backoff, if attempts remain |
| `timeout` | The attempt exceeded the job's `timeout` | Treated as retryable |
| `fatal` | A failure that won't fix itself (most 4xx, bad command) | Run `failed` immediately, no retries |

## Templates

Sink configs can include placeholders that are filled in at execution time:

| Placeholder | Value |
|---|---|
| `{{run.id}}` | Run ID, e.g. `run_01J9ZM1A…` |
| `{{run.scheduled_at}}` | Scheduled time, RFC 3339 UTC |
| `{{job.id}}`, `{{job.name}}` | The job |
| `{{attempt}}` | Attempt number (0 for the first try) |
| `{{secret.NAME}}` | A workspace [secret](../secrets/index.md). Never stored in run history or logs. |

## Why there is no MCP sink

cronpad doesn't call MCP tools directly. MCP is designed for an AI application talking to tools interactively, with session setup and user-approved OAuth tokens that expire, which doesn't suit unattended jobs, and it has no idempotency conventions. Anything you'd call on a schedule can be reached with a webhook. For the other direction, letting AI assistants manage cronpad, see the [MCP server](../ai/mcp-server.md).