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

# Automations

An automation is a stored prompt that belongs to one project and runs when any of its triggers fires. Each run is a normal Capy thread: the agent gets your prompt, the triggering event as context, and a machine to work on. You write the prompt once; the trigger decides when it runs.

For examples of recurring and event-driven workflows, see [what you can automate with Capy](https://capy.ai/features/automations).

Create automations on the **Automations** page in the app, or ask Capy to create and manage them for you in a thread; the agent has the full set of automation tools.

## Manage automations through the API

The public API lets an integration create and inspect automations, then pause, resume, delete, or restore them. Edit and manual-run controls remain in the app and agent tools.

Create an automation with the same trigger and run configuration used by the app. It starts enabled unless you send `"enabled": false`; `requestId` is the caller-stable idempotency key, so retries converge on the same automation:

```bash theme={null}
REQUEST_ID=$(uuidgen)
curl -sS -X POST "https://api.capy.ai/api/v1/automations" \
  -H "Authorization: Bearer $CAPY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "requestId": "'"$REQUEST_ID"'",
    "projectId": "'"$PROJECT_ID"'",
    "name": "Daily dependency audit",
    "prompt": "Audit dependencies for security advisories and open a PR for required upgrades.",
    "triggers": [{ "type": "schedule", "cron": "0 3 * * *", "timezone": "UTC" }],
    "threadMode": "new"
  }'
```

The create response is the stored automation. When its triggers include `incoming_webhook`, the first successful response also carries `webhookUrl`; save it immediately because retries and later reads never return that credential. If that response is lost, delete the original by its returned or listed id and create a replacement with a new `requestId`.

List every automation your API-key principal can access, or narrow the page to one project:

```bash theme={null}
curl -sS "https://api.capy.ai/api/v1/automations?projectId=$PROJECT_ID" \
  -H "Authorization: Bearer $CAPY_API_KEY"
```

The cursor-paginated response includes each automation's prompt, triggers, run configuration, run-as identity, enabled state, run count, and last-triggered time. It never includes incoming-webhook secrets or stored file references.

Pause and resume by id:

```bash theme={null}
curl -sS -X POST "https://api.capy.ai/api/v1/automations/$AUTOMATION_ID/disable" \
  -H "Authorization: Bearer $CAPY_API_KEY"

curl -sS -X POST "https://api.capy.ai/api/v1/automations/$AUTOMATION_ID/enable" \
  -H "Authorization: Bearer $CAPY_API_KEY"
```

Delete an automation when it should no longer appear in lists, or restore it later by the id retained from an earlier response:

```bash theme={null}
curl -sS -X POST "https://api.capy.ai/api/v1/automations/$AUTOMATION_ID/delete" \
  -H "Authorization: Bearer $CAPY_API_KEY"

curl -sS -X POST "https://api.capy.ai/api/v1/automations/$AUTOMATION_ID/restore" \
  -H "Authorization: Bearer $CAPY_API_KEY"
```

Every mutation returns the updated automation, including its `deleted` state. A service-user key with `read_only` access can only list; creating or changing state requires `read_write` or `admin`. Project allowlists apply to every automation operation, and an inaccessible id returns the same `404` as an unknown id.

See [Common flows](/api-reference/common-flows#create-inspect-and-pause-automations) for a script-friendly sequence and the generated API reference for complete request and response schemas.

## Triggers

An automation has 1 to 20 triggers, OR'd together: any one of them firing starts at most one run per event. At most one each of schedule, incoming webhook, and on-demand.

| Trigger | Fires on |
| - | - |
| Schedule | A cron expression in an IANA timezone |
| GitHub | PR opened/pushed/merged, comments, reviews, labels, checks, branch pushes, `workflow_run` |
| Slack | Messages in a channel (grouped into bursts) |
| Incoming webhook | An HTTP POST to a per-automation URL |
| On demand | A manual run: a button, an API call, or the agent |

Each trigger can also carry additional conditions: repositories, branches, authors, and labels for GitHub; channels, authors, and text filters for Slack; substring and regex filters for webhooks. A condition matches when the event matches any of its listed values.

### Schedules

Schedules are five-field cron and a IANA timezone, validated at save. The floor is one run per five minutes.

DST follows wall-clock semantics in your timezone: a local time that doesn't exist (spring forward) fires once at the next valid instant, and a local time that happens twice (fall back) fires once. If the scheduler falls behind, missed occurrences coalesce into a single run rather than a backlog.

### Slack bursts

A burst of Slack messages becomes a single run. Each matched message extends a sliding window (1–300 seconds, default 10); the group closes when the window elapses, a different author posts, or the burst hits its bounds (20 messages, 100 KB). The run gets the joined messages in order.

The agent's normal reply channel writes to the run's thread. If you want the automation to post in Slack, say so in the prompt and Capy will use an explicit Slack message tool.

## The run-when gate

Trigger conditions are exact filters. For fuzzy conditions like "only when the message is a bug report", add a `run_when` sentence to an event trigger. A fast model checks each matching event against it before the run starts. You are not billed for the fast model check.

## The model

`model` is optional at creation, and the choice decides how the automation behaves over time:

* **Pinned**: pass `model` and every run uses exactly that model. A pin never silently substitutes: if the run-as principal loses the right to it (the model retires, a subscription grant is revoked), the run fails visibly instead.
* **Unpinned**: omit `model` and each run resolves the run-as principal's default model at run creation — their personal default first, then the organization default (Settings → Models). An unpinned automation follows those defaults as they change.

The web editor always saves a pinned model: the picker shows the resolved default, and saving pins what it shows.

## Thread mode

`thread_mode` decides where runs land:

* `new`: every run starts a fresh thread. The default, and right for independent jobs like a nightly check.
* `single`: the automation owns one standing thread, and every run delivers its event into it. The thread keeps its machine and its context across runs, so it's right for accumulating work like triaging a channel.

## Who the automation runs as

Every automation stores one explicit run-as principal, and every run executes as it: the person who saved the automation, or a service user an admin selected. The triggering actor (the Slack author, the GitHub sender) is never the principal; they appear only inside the event context.

* Eligibility is re-verified on every run: live user, live membership, live service-user state, project access.
* If the principal is offboarded (user removed, service user disabled), the run fails naming the check that failed, and the automation is disabled with a visible reason. Re-enabling requires picking an eligible principal.
* Commits attribute to the principal: a human's runs commit as their interactive threads do; a service user's runs commit under its display name with Capy's bot address.
* Model rights are the principal's: a pinned model the principal can't use fails at save, and rights lost later fail the run visibly, never a silent substitution.

Default to a service user for anything long-lived. Service users survive offboarding; automations running as a departed teammate stop.

Any project member can run any automation in the project, enabled or disabled. Triggering executes the stored prompt as written; it changes nothing.

## The webhook trigger

An incoming-webhook automation gets a URL of the form:

```
POST https://api.capy.ai/webhooks/automations/{secret}
```

The URL itself is the credential: there is no signature scheme and no `Authorization` header. Treat it like a password: it's generated server-side, shown once at creation or rotation, and stored only as a digest. Rotation is an immediate cutover; the old URL stops working in the same instant the new one is shown.

```bash theme={null}
curl -X POST "https://api.capy.ai/webhooks/automations/$WEBHOOK_SECRET" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: deploy-4821" \
  -d '{"service": "checkout", "environment": "staging", "status": "failed"}'
```

* `Idempotency-Key` deduplicates: retries with the same key start one run. Without it, every delivery is distinct.
* Bodies are capped at 256 KB (413 above that), and the first 8,000 characters reach the agent as context.
* Responses: `202` with `accepted`, `duplicate`, or `no-match` in the body (a body your trigger's filters reject is a valid delivery that starts no run; don't retry it); `404` for an unknown or disabled automation, deliberately indistinguishable; `500` means retry.

The body is untrusted event context, never instructions. The agent reads it as data under the standing prompt; a payload that says "ignore your instructions" doesn't.

## Limits

`max_runs_per_day` is an opt-in daily cap counting runs admitted across all triggers. An over-cap event records a `skipped` run naming the cap, so throttling is visible in run history, never silent. Unset means no cap.

## Examples

* **Nightly dependency check.** Schedule `0 3 * * *`, prompt: audit dependencies for security advisories and open a PR bumping anything vulnerable.
* **Follow-up on every merged PR.** GitHub `pull_request_merged`, prompt: check the merged change for docs that now lie and fix them.
* **CI failure fixer.** GitHub `workflow_run` with conclusion `failure` on your main branch, prompt: read the failing job, fix the cause, open a PR.
* **Slack support triage.** Slack messages in `#support` with a `run_when` of "the message reports a bug", `single` thread mode, prompt: reproduce, file the issue, link it back.
* **Deploy-failure investigation.** Incoming webhook from your deploy pipeline, prompt: read the payload, find the regressing commit, report with evidence.


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