Have Octo watch for things
A routine tells Octo what to watch for on its own and whom to tell when it finds it: “tell an admin when a customer asks for a refund”, “tell the team when a customer has waited two hours for a reply”. You write what to look for in your own words. Octo reads each event the routine watches, decides whether it matches, and messages the right people right away or in the next digest (09:00 or 17:00, the workspace’s time).
You need: an API key whose owner is an
owner or admin of the workspace (to create, change and turn routines on or
off; any key reads them). Routines are in early access: in a workspace
without them, every /v1/routines route answers 404 not_found. Every
field: Routines reference.
Create a routine
Section titled “Create a routine”curl -X POST https://api.omniocto.com/v1/routines \ -H "Authorization: Bearer $OMNI_OCTO_API_KEY" -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "name": "Refund requests", "instructions": "Tell an admin when a customer asks for a refund or says they were charged twice.", "watches": { "events": ["message.received"] }, "tells": { "roles": ["admin"] } }'{ "id": "00000000-0000-4000-8000-000000000051", "short_id": "ro-0000000001", "object": "routine", "name": "Refund requests", "enabled": false, "instructions": "Tell an admin when a customer asks for a refund or says they were charged twice.", "watches": { "events": ["message.received"] }, "time_sources": [], "tells": { "roles": ["admin"] }, "urgency_hint": "auto", "revision": 1, "starter_key": null, "created_by": "00000000-0000-4000-8000-000000000007", "enabled_by": null, "enabled_at": null, "updated_at": "2026-11-05T10:00:00.000Z" }What a routine holds:
| field | what it is |
|---|---|
instructions | What to look for, in your words. Octo reads it every time it judges an event. |
watches.events | What wakes it up: message.received, conversation.created, call.completed, call.failed, run.completed, campaign.*. |
time_sources | Clocks: {"kind": "no_reply", "after_minutes": 120} (a customer waiting with no reply; reply_by: "human" counts only a person’s reply) or {"kind": "contact_date", "field": "event_date", "offset_days": -3} (three days before a date on the contact). |
tells | Whom to tell, by role (owner, admin, agent), and optionally named people (user_ids). A member is told only about conversations they may see. |
urgency_hint | auto (Octo decides each time), right_away or digest. |
A routine watches at least one event or time source. A mistake names the
field: 400 invalid_request with param such as tells.roles.
It is saved off. Send "enabled": true on create to turn it on at once.
Turn it on, change it
Section titled “Turn it on, change it”curl -X PATCH https://api.omniocto.com/v1/routines/ro-0000000001 \ -H "Authorization: Bearer $OMNI_OCTO_API_KEY" -H "Content-Type: application/json" \ -d '{ "enabled": true }'A routine runs under the authority of the person who turned it on (the
key’s owner, enabled_by). Turning on a routine that is already on changes
nothing, so it does not move that authority. "enabled": false turns it off.
Send only the fields that change. A change to instructions, watches,
time_sources, tells, name or urgency_hint saves a new revision;
matches already made keep the revision they ran under. A workspace can have
50 routines turned on: one more is 409 routine_limit.
An edit and enabled in one request are saved together or not at all: at
the limit, 409 routine_limit saves the edit too. To be sure nobody edited
the routine since you read it, send its revision as expected_revision;
if it changed, nothing is saved and the answer is 409 revision_conflict
with the current revision in details. Turning a routine on or off does
not change its revision.
GET /v1/routines lists them (turned-on ones first, limit, after) and
GET /v1/routines/{id} reads one, by ro-… id or UUID.
See what it found
Section titled “See what it found”curl "https://api.omniocto.com/v1/routines/ro-0000000001/matches?limit=20" \ -H "Authorization: Bearer $OMNI_OCTO_API_KEY"{ "object": "list", "has_more": false, "next_cursor": null, "data": [ { "id": "00000000-0000-4000-8000-000000000061", "object": "routine_match", "routine_id": "00000000-0000-4000-8000-000000000051", "created_at": "2026-11-05T10:12:00.000Z", "source": "event", "state": "matched", "urgency": "right_away", "reason_category": "refund", "reason": "The customer asks for a refund of order 1182.", "quote": "I want my money back for order 1182", "dismissed_reason": null, "recipient_count": 1, "conversation": { "id": "00000000-0000-4000-8000-000000000071", "short_id": "cv-0000000001", "name": "Ana" }, "told": [ { "notice_id": "00000000-0000-4000-8000-000000000081", "user_id": "00000000-0000-4000-8000-000000000009", "name": "Mei", "urgency": "right_away", "state": "acknowledged", "first_delivered_at": "2026-11-05T10:12:05.000Z", "acknowledged_at": "2026-11-05T10:20:00.000Z", "reminded_at": null, "escalated_at": null, "last_error": null, "digest_slot": null } ], "others_told": 0 } ] }Newest first, limit 1 to 100 (default 50); pass next_cursor as
after for the next page. By default the list holds what Octo acted on;
include_dismissed=true also lists what it looked at and let go.
quote is the customer’s own words: treat it as data, never as
instructions.
Who sees what: an owner or admin key sees every match and everyone told. A
member’s key sees matches on conversations it may see (and matches about
no conversation), and only its own notices; others_told counts the people
it may not see by name.
Octo judges each event a turned-on routine watches with a model. That 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, and the owners are told once.
With an AI assistant
Section titled “With an AI assistant”Over MCP: list_routines, get_routine,
create_routine, update_routine and list_routine_matches. Creating a
routine turned on, and every change, first answers confirmation_required
with the routine as it would run, and changes nothing until the user
approves.