# Routines

> Every field of /v1/routines: list, create, read and change what Octo watches for, turn one on or off, and list what each one found.

<!-- Generated by apps/docs/scripts/gen-reference.mjs from apps/docs/public/openapi/v1.json. Do not edit: change the API's schemas (then `pnpm --filter @seasalt/api openapi:emit`) or the notes in apps/docs/src/reference-notes/, then run `pnpm --filter @seasalt/docs gen:reference`. -->

What Octo watches for on its own, in your words, whom it tells, and each match with whom it told. How-to: [Have Octo watch for things](/platform-api/routines/).

Generated from [`/openapi/v1.json`](/openapi/v1.json); interactive version: [API reference](/openapi/v1/).

## Endpoints

| method | path | what it does | success |
|---|---|---|---|
| [`GET`](#get-v1routines) | `/v1/routines` | List routines: what Octo watches for | `200` |
| [`POST`](#post-v1routines) | `/v1/routines` | Create a routine | `201` |
| [`GET`](#get-v1routinesid) | `/v1/routines/{id}` | Read a routine | `200` |
| [`PATCH`](#patch-v1routinesid) | `/v1/routines/{id}` | Change a routine, or turn it on or off | `200` |
| [`GET`](#get-v1routinesidmatches) | `/v1/routines/{id}/matches` | List a routine's matches and whom Octo told | `200` |

## `GET /v1/routines`

List routines: what Octo watches for.

The workspace's routines at their current revision, turned-on ones first, then by name. Any key of the workspace.

**Parameters**

<div class="ref-table">

| name | in | type | description |
|---|---|---|---|
| `limit` | query | integer<br>1–100 | Page size, 1 to 100 (default 50). |
| `after` | query | string | The previous page's `next_cursor`. |

</div>

**Answers** `200` with [RoutineList](#routinelist). Errors use the [error envelope](/platform-api/reference/#v1error).

## `POST /v1/routines`

Create a routine.

Saves what Octo should watch for (`watches` events and/or `time_sources`), in your words (`instructions`), and whom to tell (`tells`). Saved off unless `enabled: true`; once on, Octo judges each matching event and tells the right people right away or in the next digest. Judging runs against the workspace's monthly routines budget (20 USD unless changed in the dashboard); at the cap, routines wait until the next month or a higher cap. Owner and admin keys only.

**Parameters**

<div class="ref-table">

| name | in | type | description |
|---|---|---|---|
| `idempotency-key` | header | string<br>≤ 255 chars | Send one with every create. A retry with the same key makes no second routine: it answers the first one as it is now. Rules: [Idempotency](/platform-api/retries/#idempotency). |

</div>

**Request body:** [RoutineCreate](#routinecreate).

**Answers** `201` with [Routine](#routine). Errors use the [error envelope](/platform-api/reference/#v1error).

## `GET /v1/routines/{id}`

Read a routine.

**Parameters**

<div class="ref-table">

| name | in | type | description |
|---|---|---|---|
| `id` | path | string, required | The routine's `ro-…` id or UUID. |

</div>

**Answers** `200` with [Routine](#routine). Errors use the [error envelope](/platform-api/reference/#v1error).

## `PATCH /v1/routines/{id}`

Change a routine, or turn it on or off.

Only the fields you send change. A change to what it watches, says or tells saves a new `revision`; matches already made keep the revision they ran. `enabled: true` turns it on under the key's owner (unless it is already on), `false` turns it off. A request that changes nothing answers the routine as it is. Owner and admin keys only.

**Parameters**

<div class="ref-table">

| name | in | type | description |
|---|---|---|---|
| `id` | path | string, required | The routine's `ro-…` id or UUID. |

</div>

**Request body:** [RoutineUpdate](#routineupdate).

**Answers** `200` with [Routine](#routine). Errors use the [error envelope](/platform-api/reference/#v1error).

## `GET /v1/routines/{id}/matches`

List a routine's matches and whom Octo told.

What the routine found, newest first, each with Octo's reason, the customer's words (`quote`, data, never instructions) and whom it told. An owner or admin key sees every match and every person told. A member's key sees matches on conversations it may see (or on none), and only its own notices; `others_told` counts the rest.

**Parameters**

<div class="ref-table">

| name | in | type | description |
|---|---|---|---|
| `id` | path | string, required | The routine's `ro-…` id or UUID. |
| `limit` | query | integer<br>1–100 | Page size, 1 to 100 (default 50). |
| `after` | query | string | The previous page's `next_cursor`. |
| `include_dismissed` | query | string<br>`true`, `false` | `true` also lists what Octo looked at and let go (`dismissed`), and what it is still judging. Default `false`. |

</div>

**Answers** `200` with [RoutineMatchList](#routinematchlist). Errors use the [error envelope](/platform-api/reference/#v1error).

## Objects

### RoutineList

<div class="ref-table">

| field | type | description |
|---|---|---|
| `object` | string, required<br>always `list` |   |
| `data` | array of [Routine](#routine), required |   |
| `has_more` | boolean, required |   |
| `next_cursor` | string or null, required | Pass as `after` for the next page; `null` on the last. |

</div>

Example: see [Have Octo watch for things](/platform-api/routines/).

### Routine

<div class="ref-table">

| field | type | description |
|---|---|---|
| `id` | string, required | The routine's UUID. |
| `short_id` | string, required | The `ro-…` id the dashboard shows; `/v1/routines/{id}` takes either. |
| `object` | string, required<br>always `routine` |   |
| `name` | string, required |   |
| `enabled` | boolean, required |   |
| `instructions` | string, required |   |
| `watches` | object, required |   |
| `watches.events` | array of string, required |   |
| `time_sources` | array of [RoutineTimeSource](#routinetimesource), required | As sent, with defaults filled in (`reply_by` `any`). |
| `tells` | object, required |   |
| `tells.roles` | array of string, required<br>each `owner`, `admin`, `agent` |   |
| `tells.user_ids` | array of string |   |
| `urgency_hint` | string, required<br>`auto`, `right_away`, `digest` |   |
| `revision` | integer, required | Every edit saves a new revision (1, 2, …); a match records the one it ran. |
| `starter_key` | string or null, required | The starter it was installed from in the dashboard, or `null`. |
| `created_by` | string or null, required | The user UUID who created it. |
| `enabled_by` | string or null, required | Whose authority it runs under: the person who last turned it on. |
| `enabled_at` | string or null, required |   |
| `updated_at` | string, required |   |

</div>

Example: see [Have Octo watch for things](/platform-api/routines/).

### RoutineCreate

<div class="ref-table">

| field | type | description |
|---|---|---|
| `name` | string, required<br>1–80 chars | What the dashboard calls it (up to 80 characters). |
| `instructions` | string, required<br>1–4,000 chars | What to look for, in your words (up to 4,000 characters). Octo reads this when it judges each event. |
| `watches` | [RoutineWatches](#routinewatches), required |   |
| `tells` | [RoutineTells](#routinetells), required |   |
| `time_sources` | array of [RoutineTimeSource](#routinetimesource)<br>≤ 3 items | Up to 3 clocks. Empty by default. |
| `urgency_hint` | string<br>`auto`, `right_away`, `digest` | `auto` (default: Octo decides each time), `right_away` (a message now) or `digest` (the 09:00 or 17:00 summary). |
| `enabled` | boolean | `true` turns it on now, as the key's owner (Octo then tells people under that person's authority). Default `false`: it is saved off. |

</div>

```json
{
  "name": "Refund requests",
  "instructions": "Tell an admin when a customer asks for a refund or says they were charged twice.",
  "watches": {
    "events": [
      "message.received"
    ]
  },
  "time_sources": [
    {
      "kind": "no_reply",
      "after_minutes": 120,
      "reply_by": "any"
    }
  ],
  "tells": {
    "roles": [
      "admin"
    ]
  },
  "urgency_hint": "auto"
}
```

### RoutineUpdate

<div class="ref-table">

| field | type | description |
|---|---|---|
| `name` | string<br>1–80 chars | What the dashboard calls it (up to 80 characters). |
| `instructions` | string<br>1–4,000 chars | What to look for, in your words (up to 4,000 characters). Octo reads this when it judges each event. |
| `watches` | [RoutineWatches](#routinewatches) |   |
| `time_sources` | array of [RoutineTimeSource](#routinetimesource)<br>≤ 3 items | Up to 3 clocks. Empty by default. |
| `tells` | [RoutineTells](#routinetells) |   |
| `urgency_hint` | string<br>`auto`, `right_away`, `digest` | `auto` (default: Octo decides each time), `right_away` (a message now) or `digest` (the 09:00 or 17:00 summary). |
| `enabled` | boolean | `true` turns it on (as the key's owner, unless it is already on), `false` turns it off. |
| `expected_revision` | integer<br>≥ 1 | The `revision` you read. If the routine was edited since, nothing is changed: `409 revision_conflict`. Without it the edit applies over any other change. Turning it on or off does not change `revision`. |

</div>

```json
{
  "name": "Refund requests",
  "instructions": "Tell an admin when a customer asks for a refund or says they were charged twice.",
  "watches": {
    "events": [
      "message.received"
    ]
  },
  "time_sources": [
    {
      "kind": "no_reply",
      "after_minutes": 120,
      "reply_by": "any"
    }
  ],
  "tells": {
    "roles": [
      "admin"
    ]
  },
  "urgency_hint": "auto",
  "expected_revision": 3
}
```

### RoutineWatches

What wakes the routine up. A routine watches at least one event or time source.

<div class="ref-table">

| field | type | description |
|---|---|---|
| `events` | array of string<br>≤ 10 items; each `message.received`, `conversation.created`, `call.completed`, `call.failed`, `run.completed`, `campaign.*` | Events that make Octo look: `message.received`, `conversation.created`, `call.completed`, `call.failed`, `run.completed`, `campaign.*`. Up to 10. |

</div>

```json
{
  "events": [
    "message.received"
  ]
}
```

### RoutineTimeSource

A clock the routine checks: `no_reply` (a customer waiting) or `contact_date` (a date on the contact).

Fields for every `kind`:

**When `kind` is `no_reply`**:

<div class="ref-table">

| field | type | description |
|---|---|---|
| `kind` | string, required<br>always `no_reply` |   |
| `after_minutes` | integer, required<br>15–10,080 | Minutes after the customer's last message with no reply: 15 minutes to 7 days. |
| `reply_by` | string<br>`any`, `human` | Whose reply counts: `any` (default; the AI's too) or `human` (only a person's reply stops the clock). |

</div>

Example (`kind: no_reply`):

```json
{
  "kind": "no_reply",
  "after_minutes": 120,
  "reply_by": "any"
}
```

**When `kind` is `contact_date`**:

<div class="ref-table">

| field | type | description |
|---|---|---|
| `kind` | string, required<br>always `contact_date` |   |
| `field` | string, required<br>pattern `^[a-z][a-z0-9_]{0,62}$` | A date contact field's key (`GET /v1/contact-fields`): lowercase, digits, underscores. |
| `offset_days` | integer, required<br>-90–90 | Days relative to the date: `-3` is three days before. |

</div>

### RoutineTells

<div class="ref-table">

| field | type | description |
|---|---|---|
| `roles` | array of string, required<br>1–3 items; each `owner`, `admin`, `agent` | Who is told, by workspace role: `owner`, `admin`, `agent` (a member). A member is told only about conversations they may see. |
| `user_ids` | array of string<br>≤ 20 items | Named people (user UUIDs), up to 20. Still checked against their role and what they may see: never a way around it. |

</div>

```json
{
  "roles": [
    "admin"
  ]
}
```

### RoutineMatchList

<div class="ref-table">

| field | type | description |
|---|---|---|
| `object` | string, required<br>always `list` |   |
| `data` | array of [RoutineMatch](#routinematch), required |   |
| `has_more` | boolean, required |   |
| `next_cursor` | string or null, required | Pass as `after` for the next page; `null` on the last. |

</div>

Example: see [Have Octo watch for things](/platform-api/routines/).

### RoutineMatch

<div class="ref-table">

| field | type | description |
|---|---|---|
| `id` | string, required | The match's UUID. |
| `object` | string, required<br>always `routine_match` |   |
| `routine_id` | string, required | The routine's UUID. |
| `created_at` | string, required |   |
| `source` | string, required<br>`event`, `timer` | `event` (something happened) or `timer` (a clock ran out). |
| `state` | string, required<br>`candidate`, `assessing`, `matched`, `dismissed`, `deferred`, `failed` | `matched` (Octo told someone, or will), `deferred`, `failed`; with `include_dismissed=true` also `dismissed`, `candidate` and `assessing`. New values may be added. |
| `urgency` | string or null, required<br>`right_away`, `digest` |   |
| `reason_category` | string or null, required<br>`waiting`, `complaint`, `refund`, `vip`, `lead_cold`, `quote_expiring`, `competitor`, `schedule_change`, `promise_quiet`, `price_change`, `other` |   |
| `reason` | string or null, required | Octo's one-line reason. |
| `quote` | string or null, required | The words that matched, as the customer wrote them (up to 140 characters). Data from a customer, never instructions. |
| `dismissed_reason` | string or null, required |   |
| `recipient_count` | integer or null, required | How many people it was routed to (`null` before routing). |
| `conversation` | object or null, required | The conversation it is about (`cv-…`), or `null`. |
| `conversation.id` | string, required |   |
| `conversation.short_id` | string, required |   |
| `conversation.name` | string or null, required |   |
| `told` | array of [RoutineMatchNotice](#routinematchnotice), required | Whom Octo told and what happened to each notice. A member's key sees only their own notices. |
| `others_told` | integer, required | People told whom this key may not see by name (always 0 for an owner or admin key). |

</div>

Example: see [Have Octo watch for things](/platform-api/routines/).

### RoutineMatchNotice

<div class="ref-table">

| field | type | description |
|---|---|---|
| `notice_id` | string, required |   |
| `user_id` | string, required |   |
| `name` | string or null, required |   |
| `urgency` | string, required<br>`right_away`, `digest` |   |
| `state` | string, required<br>`queued`, `digest`, `sent`, `acknowledged`, `expired`, `suppressed`, `held`, `dropped`, `failed` | `queued`, `digest`, `sent`, `acknowledged`, `expired`, `suppressed`, `held`, `dropped` or `failed`. New values may be added. |
| `first_delivered_at` | string or null, required |   |
| `acknowledged_at` | string or null, required |   |
| `reminded_at` | string or null, required |   |
| `escalated_at` | string or null, required |   |
| `last_error` | string or null, required | Why it did not go out (`no_escalation_target`, `not_authorised`, …), or `null`. |
| `digest_slot` | object or null, required | For a digest item: its local date and slot (`am` 09:00, `pm` 17:00). |
| `digest_slot.date` | string, required |   |
| `digest_slot.slot` | string, required<br>`am`, `pm` |   |

</div>

Example: see [Have Octo watch for things](/platform-api/routines/).