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

# Deployments & cron

> Run an agent on a schedule, then collect the results of every fire.

A **deployment** runs an [agent](/docs/concepts/agents) on a schedule. Each scheduled fire wakes the [durable runtime](/docs/concepts/runtime), starts a fresh [session](/docs/concepts/sessions), runs it to [idle](/docs/concepts/sessions#lifecycle), and stops — all within a per-run budget you set. A deployment is just a recurring session factory: everything you already know about sessions (events, files, budgets, outcomes) applies to each fire.

## Create a deployment

<CodeGroup>
  ```bash CLI theme={"system"}
  vetta deploy create --agent nightly-triage \
    --cron "0 9 * * *" \
    --budget-usd 5 \
    --window loose \
    --prompt "Summarize what changed in main overnight and open issues for regressions." \
    --identity ava-sales \
    --on-idle https://acme.dev/hooks/vetta
  ```

  ```typescript TypeScript theme={"system"}
  const deployment = await vetta.deployments.create({
    agent: agent.id,
    cron: "0 9 * * *",
    budgetUsd: 5,
    window: "loose",
    prompt: "Summarize what changed in main overnight.",
    onIdle: "https://acme.dev/hooks/vetta", // webhook fired when each run goes idle
  });
  ```
</CodeGroup>

<Tip>
  Scheduled agents are the canonical `loose` use case: an agent that starts at midnight and delivers at nine has no reason to pay the `immediate` tariff. See [Completion window](/docs/concepts/completion-window).
</Tip>

## How a fire flows

```
 cron tick ──▶ new session (deployment_id set)
                    │
                    ├─▶ events stream (resumable, per session)
                    ├─▶ published files (org-scoped, survive the session)
                    ├─▶ outcome score        (if an outcome is attached)
                    └─▶ session.idle ──▶ webhook  POST  { deployment_id, session_id, stop_reason, structured_output, outcome_evaluations[], files[] }
```

Every fire is a **normal session**. It shows up in `vetta session list`, streams the same [events](/docs/concepts/events-and-streaming), writes to the same [Files API](/docs/capabilities/files), and is subject to the same [budgets](/docs/concepts/budgets). The only difference is that it carries a `deployment_id` — and, if the deployment sets one, the `identity_id` of the [persona](/docs/identity/personas) it speaks as.

### The schedule's card on the board

If the agent is seated on a [team board](/docs/team/board), every fire runs against **one standing card
for that schedule** — not one card per fire. The card is created on the schedule's first fire, titled
`Recurring:` and the first line of the prompt, and from then on each fire re-opens the same card:
moved to `doing`, assigned to the agent, and named in the message the run starts with, so the run
reads its own history before it works and comments what it did before it finishes.

That is how scheduled work becomes visible. A card per fire would be a board row per run — a daily
schedule alone is seven a week, a crew's template is dozens — and a board nobody can read is a board
nobody uses. One card means the owner sees a schedule, with its runs as comments under it, rather
than a list that has to be scrolled past to find the work a person actually assigned.

* The card is on the board the agent is **seated on**, the one its owner reads. A schedule never
  opens a board of its own.
* The card is claimed by the fire before the session starts, so the board's own clock never starts a
  second session for it.
* An organization whose agents sit on no board sees no cards, and its schedules fire exactly as they
  did before.
* A board write that fails is logged and the fire runs anyway. The schedule is the work; the card is
  where it is shown, and the second must never be able to stop the first.

## Receiving results

There are three ways to collect what a scheduled run produced. Use the webhook for push; use listing/polling for pull; use published files for the actual artifacts.

<Steps>
  <Step title="Push — subscribe to the run finishing">
    Set `on_idle` on the deployment (or a `session.idle` [webhook](/docs/capabilities/webhooks) filtered by `deployment_id`). When a fire goes idle, Vetta POSTs a signed, [enveloped](/docs/capabilities/webhooks#delivery--verification) payload:

    ```json theme={"system"}
    {
      "id": "evt_7h...",
      "type": "session.idle",
      "created_at": "2026-08-20T09:00:12Z",
      "data": {
        "deployment_id": "dep_9f...",
        "session_id": "ses_4a...",
        "stop_reason": "end_turn",
        "consumed_micro_usd": 410000,
        "structured_output": {
          "regressions": 2,
          "issues_opened": ["ISSUE-4821", "ISSUE-4822"]
        },
        "outcome_evaluations": [
          { "outcome_id": "out_9k...", "result": "satisfied" }
        ],
        "files": ["file_1c...", "file_2d..."]
      }
    }
    ```

    Read the machine-readable result straight off `data.structured_output` — no transcript to scrape — when the agent (or deployment) has an [output schema](/docs/capabilities/structured-outputs) set; it is `null` otherwise. Fetch the deliverables by ID from the [Files API](/docs/capabilities/files). The `outcome_evaluations[]` array matches the outcomes shape on the session — one `{ outcome_id, result }` per attached outcome.

    **`on_idle` is a webhook endpoint.** Setting it creates one for that URL, so these pushes get the same signing, retries, auto-disable and [delivery log](/docs/capabilities/webhooks#delivery-log--redelivery) as any subscription. It appears in `vetta webhook list` / `GET /v1/webhooks` alongside your own, with `events: ["deployment.on_idle"]` — it is pushed by the deployment rather than subscribed, so it never also receives every other session's `session.idle`.

    ```bash theme={"system"}
    vetta webhook list                         # find the endpoint for your on_idle URL
    vetta webhook rotate <webhook_id>          # the secret to verify these deliveries with
    vetta webhook deliveries <webhook_id>      # what was sent, and whether it landed
    ```

    Change `on_idle` to move the endpoint's URL (which also re-enables it if a dead target had disabled it); set it to `null` to stop pushing.
  </Step>

  <Step title="Pull — list the runs a deployment produced">
    Each fire is a session tagged with the `deployment_id`.

    <CodeGroup>
      ```bash CLI theme={"system"}
      vetta deploy runs nightly-triage --limit 20      # sessions this deployment fired
      vetta session get $SID                 # status, stop_reason, consumed_micro_usd, structured_output
      ```

      ```typescript TypeScript theme={"system"}
      for await (const s of vetta.sessions.list({ deploymentId: deployment.id, limit: 20 })) {
        if (s.status === "idle") console.log(s.id, s.stop_reason, s.consumed_micro_usd, s.structured_output);
      }
      ```
    </CodeGroup>
  </Step>

  <Step title="Read the artifacts and the transcript">
    The durable outputs are the files a run published, its typed [`structured_output`](/docs/capabilities/structured-outputs), and its event history.

    <CodeGroup>
      ```bash CLI theme={"system"}
      vetta file list --session $SID           # what this run published
      vetta file download file_1c... > report.md
      vetta session events $SID                # full transcript (resumable with --from-seq)
      ```

      ```typescript TypeScript theme={"system"}
      const files = await vetta.files.list({ sessionId: sid });
      const report = await vetta.files.download(files[0].id);
      ```
    </CodeGroup>
  </Step>
</Steps>

<Note>
  Scheduled runs go **`idle`**, not terminated — so their files, events, and outcome scores persist and are retrievable long after the fire. Nothing is lost between the cron tick and when you read it.
</Note>

## Manage runs

```bash CLI theme={"system"}
vetta deploy list
vetta deploy runs nightly-triage         # history of fired sessions
vetta deploy run nightly-triage          # fire once, now (for testing)
vetta deploy pause nightly-triage
vetta deploy resume nightly-triage
vetta credits ledger --deployment nightly-triage   # metered spend across all fires
```

If a run would exceed the deployment budget, its calls are refused exactly as with any [budget](/docs/concepts/budgets) — the run goes idle early with `stop_reason: "budget_paused"` and still reports whatever it completed.

## Configuration reference

<ParamField path="agent" type="string" required>The agent to run on each fire.</ParamField>
<ParamField path="cron" type="string" required>A standard 5-field cron expression, evaluated in UTC.</ParamField>
<ParamField path="budget_usd" type="number" required>Per-run budget ceiling. Each fire gets a fresh allowance.</ParamField>
<ParamField path="prompt" type="string">The instruction sent to each scheduled session.</ParamField>
<ParamField path="window" type="string">Override the [completion window](/docs/concepts/completion-window) for scheduled runs — `loose` is often ideal for overnight jobs.</ParamField>
<ParamField path="identity" type="string">The [persona](/docs/identity/personas) every fire speaks as — an `idn_` id or the persona's name, the same value `session create --identity` takes. The agent must already hold a grant to it. Without one a scheduled fire runs as no persona, which is rarely what you want for an agent that owns an inbox.</ParamField>
<ParamField path="on_idle" type="string">A webhook URL POSTed when each fire goes idle, carrying an enveloped `session.idle` event whose `data` holds the run's `session_id`, `stop_reason`, `deployment_id` and typed `structured_output`. Setting it provisions a [webhook endpoint](/docs/capabilities/webhooks) for that URL — rotate it for the signing secret, and read its delivery log like any other.</ParamField>
<ParamField path="output_schema" type="object">A [JSON Schema](/docs/capabilities/structured-outputs) the run's final result must conform to, exposed as `structured_output` on each fire's idle session and webhook payload. Overrides the agent's default schema.</ParamField>
<ParamField path="outcome" type="string">An outcome to grade every fire against a rubric.</ParamField>
<ParamField path="metadata" type="object">Arbitrary key-value pairs copied onto each session this deployment creates.</ParamField>

<Card title="Next: webhooks" icon="bell" href="/docs/capabilities/webhooks">
  The full event catalog, delivery, retries, and signature verification.
</Card>
