# Actions

> Webhooks, API requests, emitted events, and presets: what a trigger does.

A trigger's `then` is one action or a list; every action runs.

## Webhook

```json
{"webhook": {"url": "https://example.com/hooks/coordinate", "signing_secret": "secret:HOOK"}}
```

For a receiver built for Coordinate. The body is the firing as JSON: `{version: "coordinate.webhook.v1", delivery_id, trigger, firing, event, rows, row, about}`. With a signing secret, `Coordinate-Signature: t=<unix>,v1=<hex HMAC-SHA256 of "<t>.<body>">`.

## API request

```json
{"request": {"method": "POST", "url": "https://api.linear.app/graphql",
   "auth": {"bearer": "secret:LINEAR"},
   "body": {"query": "mutation($t: String!) { issueCreate(input: {teamId: \"T\", title: $t}) { issue { id } } }", "variables": {"t": "{{event.payload.title}}"}}},
 "record": {"issue_id": "data.issueCreate.issue.id"}}
```

For any API, in its own shape. `auth` is `{"bearer": "secret:X"}`, `{"headers": {"x-api-key": "secret:X"}}`, or `{"connection": "github" | "<source>"}` to use a connected source's credential. The host can't be templated, and a secret is only ever sent to the host it was first used with. With `{"connection": "github"}`, Coordinate uses the GitHub App installation for the account the URL names (`/repos/{owner}/…`), for any endpoint the app's permissions cover: metadata (stars, releases, topics), contents, pull requests, checks, and Actions. Reads (`GET`) of public repositories the app isn't installed on use Coordinate's own GitHub credentials, so watching any public repository needs no token. Only a private repository the app can't reach needs `{"bearer": "secret:NAME"}` with a personal access token. `record` copies fields from the JSON response into `action.succeeded`.

## Emit

```json
{"emit": {"type": "deploys.flaky", "payload": {"repo": "{{event.payload.repo}}"}}}
```

Appends an event on source `coordinate` right away, for other triggers and agents to react to.

## Messages

Send a message to wherever you'll see it. Email to yourself needs no setup; for the others, store the credential as a secret first (Settings → Secrets, or `coordinate secret set NAME`).

```json
{"slack": {"webhook_url": "secret:SLACK_WEBHOOK", "message": "Deploy failed: {{event.payload.url}}"}}
{"discord": {"webhook_url": "secret:DISCORD_WEBHOOK", "message": "Deploy failed: {{event.payload.url}}"}}
{"email": {"subject": "{{trigger.name}}", "message": "Deploy failed: {{event.payload.url}}"}}
{"email": {"api_key": "secret:RESEND", "from": "Coordinate <alerts@your-domain.com>", "to": ["team@example.com"], "subject": "{{trigger.name}}", "message": "Deploy failed: {{event.payload.url}}"}}
{"poke": {"message": "Deploy failed: {{event.payload.url}}", "api_key": "secret:POKE"}}
```

- **Slack**: in Slack, create an app with an incoming webhook for the channel (api.slack.com/apps → Incoming Webhooks) and store the `https://hooks.slack.com/…` URL as a secret. The URL is the credential, so it is never shown again.
- **Discord**: channel settings → Integrations → Webhooks → New webhook → Copy URL, and store the `https://discord.com/api/webhooks/…` URL as a secret. Mentions in the message don't ping anyone.
- **Email**: with just `subject` and `message`, Coordinate sends it from its own address to your workspace's email (Settings → Email), up to 50 a day. To email anyone else, or more, add your Resend API key as `api_key`, a sender on a domain verified in Resend as `from`, and the recipients as `to`.
- **Poke**: a text through Poke, framed so Poke knows it came from an automation.
- **Anything else** (ntfy, PagerDuty, Telegram, your own app): a `webhook` with the whole firing as JSON, or a `request` in the API's own shape.

## Claude agent
- `{"claude_agent": {"api_key": "secret:ANTHROPIC", "agent_id": "...", "environment_id": "...", "vault_ids": [...], "repo": "owner/name", "repo_token": "secret:GH", "rubric": "...", "message": "..."}}`: starts a Claude Managed Agents session with the task as an outcome, the firing as data, and instructions to report back with `emit` (`agent.completed` or `agent.failed` with the firing id as `correlation_id`). The session id is recorded.

## Templates

`{{event.payload.x}}`, `{{event.id}}`, `{{rows}}`, `{{row.x}}`, `{{trigger.name}}`, `{{firing.id}}`, with `|lower` and `|upper`. A string that is exactly one placeholder keeps the value's type, so `"{{event.payload}}"` stays an object.

## Delivery

Webhooks, requests, messages, and agent sessions are delivered at least once: up to 10 attempts over 24 hours, retrying network errors, 408, 429, and 5xx with backoff (honoring `Retry-After`). Redirects are not followed. A failure that won't recover becomes an alert. `list_actions` shows recent runs; `retry_action` sends a failed one again.
