# Automations — Umney Connect API

> For AI agents: Automations workflows, runs, skills, sessions, agents, and `automations:manage` tokens.
> Prefer customer-facing names (Automations). Do not expose internal billing catalog IDs.
> **Live runner + agent OS.** Third parties inject `context` — Connect does **not** crawl external systems.
> Sessions, agents, memory, approvals, channels, marketplace, and SLO are available on the public Automations API.

## Quick facts

| Item | Value |
|---|---|
| Service | Automations |
| Status | Public Automations API **live** · runner + control plane + agent OS |
| HTML docs | https://umneyconnect.com/developers/automations |
| OpenAPI | https://umneyconnect.com/developers/openapi-automations.json |
| Token guide | https://umneyconnect.com/developers/api-tokens |
| Dashboard | Dashboard → Automations (Control UI) |
| Permissions | `automations:manage` |
| Live token | `umk_live_automations_<prefix>_<secret>` |
| Test token | `umk_test_automations_<prefix>_<secret>` |
| Production API | https://umneyconnect.com/api |

## Create access

1. Enable **Automations** in Billing. Enable skill products you need (AI Agent, Business Email, Marketing, Live Chat, Business Phone).
2. Dashboard → Developers → create token → product **Automations** → `automations:manage`.
3. Optional: `GET /v1/automations/onboarding` for the time-to-first-run checklist.
4. Store secret server-side only.

```
CONNECT_API_BASE=https://umneyconnect.com/api
AUTOMATIONS_API_KEY=umk_live_automations_…
```

## Public Automations API (token — live)

| Method | Path | Permission | Status |
|---|---|---|---|
| GET\|POST | `/v1/automations` | automations:manage | **Live** (alias → workflows) |
| GET\|POST | `/v1/automations/workflows` | automations:manage | **Live** |
| PATCH\|DELETE | `/v1/automations/workflows/{id}` | automations:manage | **Live** |
| POST | `/v1/automations/workflows/{id}/run` | automations:manage | **Live** |
| GET | `/v1/automations/runs` | automations:manage | **Live** |
| GET | `/v1/automations/runs/{id}` | automations:manage | **Live** |
| POST | `/v1/automations/hooks/inbound` | automations:manage or webhook token | **Live** |
| POST | `/v1/automations/runs/{id}/cancel` | automations:manage | **Live** |
| POST | `/v1/automations/runs/{id}/retry` | automations:manage | **Live** |
| GET | `/v1/automations/flows` | automations:manage | **Live** |
| GET | `/v1/automations/flows/{id}` | automations:manage | **Live** |
| GET\|PUT | `/v1/automations/policies` | automations:manage | **Live** (standing orders) |
| GET\|POST | `/v1/automations/sessions` | automations:manage | **Live** |
| GET | `/v1/automations/sessions/{id}` | automations:manage | **Live** |
| GET\|POST | `/v1/automations/agents` | automations:manage | **Live** |
| PATCH\|DELETE | `/v1/automations/agents/{id}` | automations:manage | **Live** |
| GET\|PUT | `/v1/automations/memory` | automations:manage | **Live** |
| GET | `/v1/automations/approvals` | automations:manage | **Live** |
| POST | `/v1/automations/approvals/{id}/decide` | automations:manage | **Live** |
| GET\|POST | `/v1/automations/tool-profiles` | automations:manage | **Live** |
| GET\|PUT | `/v1/automations/models` | automations:manage | **Live** |
| GET\|POST | `/v1/automations/channels` | automations:manage | **Live** |
| POST | `/v1/automations/channels/{id}/inbound` | channel secret or API key | **Live** |
| GET\|POST | `/v1/automations/nodes` | automations:manage | **Live** (companion pairing) |
| POST | `/v1/automations/nodes/claim` | pairing code | **Live** (device) |
| PATCH | `/v1/automations/nodes/{id}` | automations:manage | **Live** (allowSystem/allowBrowser) |
| DELETE | `/v1/automations/nodes/{id}` | automations:manage | **Live** |
| POST | `/v1/automations/nodes/{id}/heartbeat` | device secret | **Live** |
| GET | `/v1/automations/nodes/{id}/commands` | device secret | **Live** |
| POST | `/v1/automations/nodes/{id}/commands/{id}/result` | device secret | **Live** |
| WS | `/v1/automations/nodes/{id}/ws?secret=` | device secret | **Live** (DO push) |
| GET | `/v1/automations/skills` | automations:manage | **Live** (marketplace) |
| POST | `/v1/automations/skills/install` | automations:manage | **Live** |
| POST | `/v1/automations/skills/{packId}/workflow` | automations:manage | **Live** |
| GET\|POST | `/v1/automations/skills/sdk` | automations:manage | **Live** (partner SDK) |
| GET | `/v1/automations/slo` | automations:manage | **Live** |
| GET | `/v1/automations/onboarding` | automations:manage | **Live** |
| GET\|POST | `/v1/automations/sequences` | automations:manage | **Live** |
| PATCH\|DELETE | `/v1/automations/sequences/{id}` | automations:manage | **Live** |
| POST | `/v1/automations/sequences/{id}/run` | automations:manage | **Live** (materialize workflow + enqueue) |
| GET | `/v1/automations/messages` | automations:manage | **Live** |
| GET | `/v1/automations/logs` | automations:manage | **Live** |

### Create workflow + run

```bash
curl -X POST "https://umneyconnect.com/api/v1/automations/workflows" \
  -H "Authorization: Bearer $AUTOMATIONS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Lead enrich",
    "active": true,
    "triggerJson": { "schemaVersion": 1, "type": "manual" },
    "stepsJson": [
      { "type": "skill", "skillId": "ai.invoke", "input": { "input": "Draft a follow-up", "context": "{{context}}" } },
      { "type": "skill", "skillId": "log.write", "input": { "message": "{{last.outputText}}" } }
    ]
  }'

curl -X POST "https://umneyconnect.com/api/v1/automations/workflows/$ID/run" \
  -H "Authorization: Bearer $AUTOMATIONS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"context":"Lead from Nest: priority high","payload":{"leadId":"99"}}'
```

## Control plane + agent OS (live)

| Feature | Behavior |
|---|---|
| Task Flow | Every run links to an `automation_flows` row with `revision` + `state_json` |
| Cancel / Retry | `POST …/runs/{id}/cancel\|retry` |
| Standing orders | `PUT /policies` injects into `ai.invoke` / agent steps + `{{policy}}` |
| Heartbeat skip | Empty checklist + empty log-only steps → `empty-heartbeat` |
| Busy defer | Schedule/heartbeat skipped when ≥3 `running` runs |
| Sessions | Transcript + compaction; scopes `dm`/`group`/`run`/`channel`/`workspace` |
| Agents | Profiles + `route_keys` multi-agent routing |
| Memory | Audited `memory.read` / `memory.write` (+ `/memory` API) |
| Approvals | Step type `approval` or tool-profile gate → `waiting_approval` until `/decide` |
| Subagents | `subagent.spawn` enqueues child run with `parent_run_id` |
| Tool profiles | Allow / deny / require-approval skill lists |
| Models | Primary + failover labels (`PUT /models`); Workers AI is the live provider |
| Channels | Slack HMAC / Telegram secret / WhatsApp + media resolve + pairing allowlist |
| Companion nodes | Pairing, device secret, command RPC, WS push DO, screen/camera/audio/files |
| Marketplace + SDK | Sandboxed packs; partner publish via `/skills/sdk` |
| SLO | `GET /slo` success rate + p95 start latency (7d) |
| Control UI | Dashboard → Automations |

## Triggers

| `trigger_json.type` | Behavior |
|---|---|
| `manual` | `POST …/run` or inbound |
| `schedule` | `everySeconds` interval, one-shot `at`, or 5-field UTC `cron` (minutely Worker drain) |
| `heartbeat` | Ambient checklist schedule (interval or cron) |
| `event` | Live Chat events (`conversation.created`, `message.created`, …) |
| `webhook` | Inbound hook; optional `trigger_json.webhook.token` |
| `channel` | Inbound via `POST /v1/automations/channels/{id}/inbound` |

## Skills (live)

| skillId | Requires | Notes |
|---|---|---|
| `log.write` | Automations | Audit row |
| `ai.invoke` | AI Agent | Model failover via `/models` |
| `email.send` | Business Email | `to[]`, `subject`, `html`/`text`/`body` |
| `marketing.campaign_send` | Marketing + Business Email | `campaignId` |
| `chat.post_message` | Live Chat | `channelId`, `content` |
| `chat.assign` | Live Chat | `channelId`, `assigneeId` |
| `webhook.deliver` | Automations | HTTPS POST to partner systems |
| `memory.read` / `memory.write` | Automations | Key/value memory |
| `channel.deliver` | Channel credentials | Slack/Telegram/WhatsApp/webhook/**email** outbound |
| `voice.usage_read` | Business Phone | Safe usage read |
| `subagent.spawn` | Automations | Child workflow run |
| `node.capture_screen` | Automations + paired node | OS screenshot (companion) |
| `node.capture_camera` | Automations + paired node | Requires ffmpeg on companion |
| `node.capture_audio` | Automations + paired node | Requires ffmpeg on companion |
| `node.ping` / `node.files_list` / `node.files_read` | Automations + paired node | `nodeId`, `path?` |
| `system.run` | Node with `allowSystem` + approval | `nodeId`, `argv[]` (no shell) |
| `browser.navigate\|screenshot\|click\|type\|evaluate\|content` | Node with `allowBrowser` | `nodeId` + action fields |

`system.run` / `browser.navigate` / `browser.evaluate` **require approval** by default (tool profile can opt out with `requireApprovalSkills: ["!system.run"]`).
Channel inbound requires `config_json.inboundSecret` (min 16 chars).

## Companion nodes (live)

1. `POST /v1/automations/nodes` → receive `pairingCode` (15 min TTL). Set `allowSystem` / `allowBrowser` + allowlists to enable acting tools.
2. Device `POST /v1/automations/nodes/claim` with code → `deviceSecret` (store once).
3. Run `tools/umney-companion-node/agent.mjs run` (real `execFile` + optional Playwright).
4. Device heartbeats + polls `/commands` or opens `WS …/nodes/{id}/ws?secret=`.
5. Workflow skills enqueue commands; device posts `/commands/{id}/result` (optional base64 → R2).
6. Capabilities + `allow_system` / `allow_browser` + binary/host allowlists; revoke clears secret.

## Skill SDK (partners — live)

- `GET /v1/automations/skills/sdk` — allowlisted skills schema (no arbitrary edge code).
- `POST /v1/automations/skills/sdk` — validate + publish sandboxed pack into marketplace.

Step types: `skill` | `deliver` | `log` | `agent` (tool-calling loop) | `approval`.

Agent protocol: reply `TOOL_CALL:<skillId>|<json>` or `FINAL:<text>`.

Templates: `{{context}}`, `{{payload.*}}`, `{{event.type}}`, `{{policy}}`, `{{last.outputText}}`, `{{step0.*}}`.

## Third-party systems

Connect **does not crawl** Nest/CRM/ERP. Your backend:

1. Fetches partner data with **that system's** credentials.
2. Injects via `context` / `payload` on `/run` or `/hooks/inbound`.
3. Optionally receives results via `webhook.deliver` or `channel.deliver`.

```bash
curl -X POST "https://umneyconnect.com/api/v1/automations/hooks/inbound" \
  -H "Authorization: Bearer $AUTOMATIONS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "workflowId":"YOUR_WORKFLOW_UUID",
    "event":"nest.case.updated",
    "context":"Case #441 status=open priority=high",
    "payload":{"caseId":"441"},
    "idempotencyKey":"nest-441-open"
  }'
```

Webhook-token auth (no API key): set `trigger_json.webhook.token` (≥16 chars) and send `X-Umney-Automation-Token`.

## Dashboard Growth API (JWT — live)

| Method | Path |
|---|---|
| GET\|POST | `/api/growth/automations/workflows` |
| GET\|POST | `/api/growth/automations/sequences` |
| GET | `/api/growth/automations/console` |
| GET\|POST | `/api/growth/automations/agents` |
| GET | `/api/growth/automations/runs` |
| GET | `/api/growth/automations/sessions` |
| GET | `/api/growth/automations/approvals` |
| GET | `/api/growth/automations/channels` |
| PATCH\|DELETE | `/api/growth/automations/{resource}/{id}` |
| GET | `/api/growth/entitlements` |

## Errors

- `403` — Automations / skill product not entitled / wrong token / tool profile deny
- `401` — inbound token mismatch
- `400` — inactive workflow, empty steps, bad skill input
- `404` — workflow/run/session not found
- `502` — skill failure (provider / partner HTTP)

## Agent checklist

1. `/llms.txt` → this file
2. Enable Automations (+ skill products) → create token
3. Optional: install marketplace pack (`pack.log-echo`) → workflow
4. Create **active** workflow with real `stepsJson` skills
5. Inject third-party `context`; do not invent crawl
6. Poll `/v1/automations/runs/{id}` for `stepsResultJson`
7. Check `/v1/automations/slo` before any industry claim
8. Do not expose internal billing catalog IDs

## Related

- AI Agent — `ai.invoke` + `/v1/ai/runs`
- Business Email — `email.send`
- Marketing — `marketing.campaign_send`
- Live Chat — event triggers + `chat.post_message`
- Business Phone — `voice.usage_read`
