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

# Workflows

> Author, activate, and run workflow graphs over the API: full-replace writes, optimistic concurrency, and real deliveries.

A workflow is a **graph document** plus a set of **triggers**. Both are replace-sets: every write sends the complete desired state, and anything you leave out is deleted. Read first, change what you mean to, send the whole thing back.

The product-side walkthrough is [Workflows](/features/workflows). This page covers what the contract can't tell you. Activation, runs, and run history are shown here as [sumcli](/api/cli) commands, which handle the optimistic-concurrency and confirmation flags for you.

<Card title="POST /v1/workflows" icon="code" href="/api-reference/workflows/create-workflow" horizontal>
  Create a workflow from a graph document.
</Card>

<Warning>
  A `403` here usually means the workflows API is not open for the tenant, not that your request is malformed. Access to workflows in the app does not by itself open the API.
</Warning>

## Schedule

In the graph, this is the **Schedule** node: `summation.trigger.schedule/v1`. Its own config is empty; the cadence itself lives in the request's `triggers` array, alongside the graph. The node says *that* the workflow is scheduled, and the trigger row says *when*. **The two halves have to agree:** include the schedule trigger node exactly when the request also carries triggers, or the write is refused.

```json theme={null}
[
  { "label": "Weekday mornings",
    "schedule": { "type": "daily", "zone_id": "America/Los_Angeles", "time_of_day": "09:00" } }
]
```

Cadence `type` is one of `cron`, `interval`, `one_time`, `daily`, `weekly`, `biweekly`, `monthly`, `month_end`, or `yearly`, with the matching fields set (`days_of_week` in UPPERCASE for weekly and biweekly, `cron_expression` for cron, `every_minutes` for interval).

<Warning>
  Triggers are a **full replace-set on every write**. A workflow's triggers are exactly the ones the request carries; any you omit are deleted along with their backing schedule. Echo back the `id` of every trigger you mean to keep.
</Warning>

Only **schedule** triggers can be created through the API today.

## Playbook

In the graph, this is the **Run playbook** node: `summation.playbook.run/v1`. Config: `playbook_file_id` (required), `params`, `output_ids`.

## Delivery

| In the editor               | Node type                     | What it does                                                                                                            |
| --------------------------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| **Send an email**           | `summation.delivery.email/v1` | Emails the produced files. Config: `recipients` (required) and nothing else; the platform renders the subject and body. |
| **Send Slack notification** | `summation.delivery.slack/v1` | Posts the produced files to Slack. Config: `targets` and `slack_team_id`, both required.                                |

## Create a workflow

<Card title="POST /v1/workflows" icon="code" href="/api-reference/workflows/create-workflow" horizontal>
  Create a workflow from a graph document.
</Card>

The `graph` is the **full desired document on every write, not a patch**: what you send replaces what is stored. It must contain a trigger node wired to the first work node:

```json theme={null}
{
  "nodes": [
    { "key": "schedule_trigger", "type": "summation.trigger.schedule/v1", "config": {} },
    { "key": "playbook", "type": "summation.playbook.run/v1",
      "config": { "playbook_file_id": "file-...", "params": {} } },
    { "key": "email", "type": "summation.delivery.email/v1",
      "config": { "recipients": ["team@example.com"] } }
  ],
  "edges": [
    { "from_node": "schedule_trigger", "from_port": "inputs",
      "to_node": "playbook", "to_port": "inputs" }
  ]
}
```

Get `playbook_file_id` from `GET /v1/projects/{project_id}/playbooks`. The document serializes to at most 200,000 characters. Graphs authored through the API are adopted by the web editor, which normalizes them on its next save.

```bash theme={null}
sumcli workflows create --title "Weekly executive report" \
  --graph-file ./graph.json --triggers-file ./triggers.json \
  --output-folder /Reports
```

## Activate, then run

```bash theme={null}
sumcli workflows activate <workflow-id> --expected-revision 3 --confirm
sumcli workflows versions <workflow-id>
```

`expected_revision` is optimistic concurrency: pass the `revision` from your last read and the write is refused if the workflow moved on in between. This matters because triggers and the graph are replace-sets: without it, two concurrent edits silently lose one.

To run one immediately, outside its schedule:

```bash theme={null}
sumcli workflows run <workflow-id> --confirm
```

<Warning>
  Both `activate` and `run` require `--confirm`, because both send **real email and Slack** to the recipients the graph names. Activating starts the cadence; running executes the delivery steps now.
</Warning>

Pass `--request-id <uuid>` to make a run idempotent: retry with the same value and you get the same run, not a second one. A run needs an activated version; the CLI reads `activeVersionId` for you when you omit `--version`.

## Watch what happened

```bash theme={null}
sumcli workflows runs <workflow-id>
sumcli workflows run-show <workflow-id> <run-id>
```

A run is `pending`, `dispatched`, `running`, then `succeeded`, `failed`, `canceled`, or `timed_out`. Each node in the run reports its own status, and a node that never ran reads `skipped` with the reason it was passed over.
