Skip to main content

flow-automation

Build and run Directus flows, covering event hooks, schedules, webhook and manual triggers, operations such as condition, request, read and update data, and the data chain that passes values between operations. Use it for any Directus question about creating, wiring, scheduling or triggering an automation flow, in any language. For a flow that does not run or a condition that never matches use troubleshoot.

Source facts

Repository
Agents-Store/codex-plugins
Last source activity
November 1, 2026 at 02:46
Detected SKILL.md language
English
Stars
0
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
5 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
flow-automation
description
Build and run Directus flows, covering event hooks, schedules, webhook and manual triggers, operations such as condition, request, read and update data, and the data chain that passes values between operations. Use it for any Directus question about creating, wiring, scheduling or triggering an automation flow, in any language. For a flow that does not run or a condition that never matches use troubleshoot.
# Flow Automation Complete reference for the `flows`, `operations`, and `trigger-flow` MCP tools. ## Concepts - **Flow** = Trigger + Chain of Operations - Each flow has ONE trigger and a series of operations forming a data chain - Operations connect via `resolve` (success) and `reject` (failure) paths - Data passes through the chain using `{{ operation_key }}` variables ## Trigger Types | Trigger | When It Fires | Key Options | |---------|---------------|-------------| | `event` | On item CRUD (hook) | `type` (filter/action), `scope` (items.create, items.update, items.delete), `collections` | | `webhook` | HTTP request to flow URL | `method` (GET/POST), `async` | | `schedule` | Cron expression | `cron` (e.g., `"0 9 * * 1"` = Monday 9am) | | `operation` | Called by another flow | — | | `manual` | User triggers from app | `collections`, `requireSelection`, `fields` | ## Creating a Flow ```text Tool: flows Input: { "action": "create", "data": { "name": "Notify on New Post", "trigger": "event", "status": "active", "accountability": "all", "icon": "notifications", "color": "#FF9800", "description": "Send notification when a new post is created", "options": { "type": "action", "scope": ["items.create"], "collections": ["posts"] } } } ``` ### Trigger Options by Type **Event trigger:** ```jsonc "options": { "type": "action", "scope": ["items.create", "items.update"], "collections": ["posts"] } ``` - `type: "filter"` — runs before the operation (can block it) - `type: "action"` — runs after the operation **Schedule trigger:** ```jsonc "options": { "cron": "0 */6 * * *" } ``` **Manual trigger:** ```jsonc "options": { "collections": ["posts"], "requireSelection": true, "fields": [ { "field": "reason", "type": "string", "name": "Reason" } ] } ``` **Webhook trigger:** ```jsonc "options": { "method": "POST", "async": false } ``` ## Operation Types | Type | Description | Key Options | |------|-------------|-------------| | `condition` | If/else branching | `filter` (condition rules) | | `item-create` | Create items | `collection`, `payload` | | `item-read` | Read items | `collection`, `query` | | `item-update` | Update items | `collection`, `payload`, `key` or `query` (see below) | | `item-delete` | Delete items | `collection`, `key` or `query` (see below) | | `mail` | Send email | `to`, `subject`, `body` | | `notification` | In-app notification | `recipient`, `subject`, `message` | | `request` | HTTP request | `url`, `method`, `headers`, `body` | | `sleep` | Wait/delay | `milliseconds` | | `log` | Log to console | `message` | | `exec` | Run custom code | `code` | | `transform` | Transform data | `json` | | `trigger` | Chain to another flow | `flow` | | `json-web-token` | Sign, verify or decode a JWT | `operation` (`sign`/`verify`/`decode`), `payload`, `token`, `secret`, `options` | | `throw-error` | Fail the flow with a custom error | `code`, `status`, `message` | ### Targeting in `item-update` and `item-delete` (Directus 12.3.0+) Target items with **either** `key` (one primary key or an array) **or** `query`, never both: - Empty or missing `key` and `query` (including `"query": {}`) return `null` and change nothing. Before 12.3.0 they affected every item in the collection. - To update or delete **all** items, say so explicitly with `"query": { "limit": -1 }`. - `key` together with `query` fails the operation with `INVALID_PAYLOAD` ("Cannot use both key and query"). `item-update` with a batch (array) payload together with `key` or `query` throws too, because a batch payload carries its own keys. - 12.4.0+: update and delete by `query` apply the **read** permissions of the operation's accountability. The role needs read on the primary key, and only readable items are touched. Fields used in `filter` and `sort` must be readable. - Both operations also take `emitEvents` (fire hooks and flows for the change) and `permissions` (`$trigger`, `$full`, `$public` or a role UUID) to choose whose permissions apply. ```text Tool: operations Input: { "action": "create", "data": { "flow": "FLOW_UUID", "key": "archive_old_drafts", "type": "item-update", "name": "Archive old drafts", "options": { "collection": "posts", "payload": { "status": "archived" }, "query": { "filter": { "date_created": { "_lt": "$NOW(-90 days)" }, "status": { "_eq": "draft" } }, "limit": -1 } }, "position_x": 40, "position_y": 1 } } ``` A `query` without a `limit` is capped at `QUERY_LIMIT_DEFAULT` (100 unless configured), so a bulk change over more matches needs `"limit": -1` as above (bounded by `QUERY_LIMIT_MAX` when that is set). With the `filter` this still targets only old drafts. A bare `{"limit": -1}` with no `filter` means the whole collection, so use it only on purpose. ## Creating Operations ```text Tool: operations Input: { "action": "create", "data": { "flow": "flow-uuid-here", "key": "check_status", "type": "condition", "name": "Check if Published", "options": { "filter": { "$trigger": { "payload": { "status": { "_eq": "published" } } } } }, "position_x": 20, "position_y": 1, "resolve": "next-op-uuid-or-null", "reject": null } } ``` ## Connecting Operations Operations form a chain through `resolve` and `reject` UUIDs: 1. Create all operations first (with `resolve: null`, `reject: null`) 2. Update each operation with the correct `resolve`/`reject` UUIDs 3. Update the flow with `operation: "first-op-uuid"` to set the entry point ```text Tool: flows Input: { "action": "update", "key": "flow-uuid", "data": { "operation": "first-operation-uuid" } } ``` ## Data Chain Variables Access data from triggers and previous operations: | Variable | Source | |----------|--------| | `{{ $trigger }}` | Trigger data (payload, keys, collection) | | `{{ $trigger.payload }}` | Item data that triggered the flow | | `{{ $trigger.keys }}` | Primary keys of items | | `{{ $trigger.collection }}` | Collection name | | `{{ $trigger.body }}` | Webhook/manual trigger body | | `{{ operation_key }}` | Output of a specific operation (use the `key` you set) | | `{{ $accountability }}` | User who triggered the flow | ### Critical Syntax Rules 1. **Condition filters** — Use nested objects, NOT dot notation: ```jsonc // CORRECT { "$trigger": { "payload": { "status": { "_eq": "published" } } } } // WRONG { "$trigger.payload.status": { "_eq": "published" } } ``` 2. **Request headers** — Array of `{header, value}` objects, NOT key-value: ```jsonc // CORRECT "headers": [ { "header": "Content-Type", "value": "application/json" }, { "header": "Authorization", "value": "Bearer token" } ] // WRONG "headers": { "Content-Type": "application/json" } ``` 3. **Request body** — Stringified JSON, NOT native objects: ```jsonc // CORRECT "body": "{\"message\": \"{{ $trigger.payload.title }}\"}" // WRONG "body": { "message": "{{ $trigger.payload.title }}" } ``` 4. **Data chain references** — Use explicit operation keys: ```jsonc // CORRECT (using operation key) "{{ read_user.email }}" // AVOID "{{ $last }}" ``` ## Complete Flow Example ### "Send Slack Notification on New Published Post" **Step 1: Create the flow:** ```text Tool: flows Input: { "action": "create", "data": { "name": "Notify Slack on Publish", "trigger": "event", "status": "active", "options": { "type": "action", "scope": ["items.create", "items.update"], "collections": ["posts"] } } } ``` **Step 2: Create condition operation:** ```text Tool: operations Input: { "action": "create", "data": { "flow": "FLOW_UUID", "key": "is_published", "type": "condition", "name": "Is Published?", "options": { "filter": { "$trigger": { "payload": { "status": { "_eq": "published" } } } } }, "position_x": 20, "position_y": 1 } } ``` **Step 3: Create HTTP request operation:** ```text Tool: operations Input: { "action": "create", "data": { "flow": "FLOW_UUID", "key": "send_slack", "type": "request", "name": "Send to Slack", "options": {
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub