> ## 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.

## 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>
