# Automation Guide Automations (workflows) are event-driven: a **trigger** fires, then **actions** run in topological order. inkreach-teable's automation engine is self-built — use the `inkreach-teable` CLI (not official `teable`). > **Required reading before configuring triggers**: [trigger-types.md](../api-reference/trigger-types.md) > **Required reading before writing script actions**: [action-types.md](../api-reference/action-types.md) ## Mental Model ``` [Trigger] → [Action 1] → [Action 2] → ... → [triggerEnd] ``` - A workflow has **exactly one trigger node** (type `trigger`) and N action nodes (type `action`). - Nodes connect via **edges** (linear chain; each node has at most one upstream — no parallel branches). - **Activating** freezes a snapshot; execution runs against the snapshot, not the draft. - `hasDraft: true` = unactivated changes exist; re-`activate` to apply. ## Available Commands | Command | Purpose | |---------|---------| | `workflow list` | List all workflows in the base | | `workflow get ` | Detail (nodes + edges + config) | | `workflow create --name ` | Create (auto-adds a recordCreated trigger) | | `workflow update [--name] [--description]` | Rename | | `workflow delete ` | Soft-delete | | `workflow activate ` | Snapshot + register trigger + create auto-token | | `workflow deactivate ` | Unregister trigger | | `workflow test ` | Mock-input test run (no trigger, no queue) | | `trigger set --type ...` | Set trigger type + config | | `action add --type ...` | Add an action node | | `action update ...` | Update an action node | | `action delete ` | Remove an action node | | `secret set/list/delete` | Manage script secrets | | `runs list/get/rerun` | Execution history | | `fire button-click/webhook` | Manually trigger for testing | ## Trigger Selection | User scenario | `--type` | Key options | |--------------|----------|-------------| | React to new records | `recordCreated` | `--table-id` | | React to record changes | `recordUpdated` | `--table-id`, `--field-ids` (empty = all) | | React when a condition matches | `recordCondition` | `--table-id`, `--conditions` (required) | | React to create OR update | `recordCreatedOrUpdated` | `--table-id`, `--conditions` (optional) | | Form submission | `formSubmitted` | `--table-id`, `--form-ids ` | | Schedule (only type+interval work) | `scheduledTime` | `--schedule '{"timing":{"type":"minutes","interval":5}}'` | | Button field click | `buttonClick` | `--table-id`, `--field-ids` | | External HTTP POST | `webhook` | (none; auth backend-ignored) | **Batch processing** (record triggers only): add `--batch '{"enabled":true,"batchWindow":2000,"batchSize":100}'` to merge events within a window into a single run. The script receives an array of records. Full config details: [trigger-types.md](../api-reference/trigger-types.md). ## Action Selection | Goal | `--type` | Key options | |------|----------|-------------| | Run JS code (transform, call APIs, compute) | `script` | `--code ''` or `--code-file `, `--timeout ` | | Send email | `sendEmail` | `--to`, `--subject`, `--body ` | | Call external HTTP | `httpRequest` | `--url`, `--method`, `--headers `, `--req-body ` | | Create records | `createRecord` | `--target-table `, `--records ` | | Update records | `updateRecord` | `--target-table `, `--records ` | | Query records | `getRecords` | `--target-table `, `--query ` | Full config details: [action-types.md](../api-reference/action-types.md). ## Creation Workflow ```bash # 1. Gather IDs inkreach-teable table get tblXXX # confirm table inkreach-teable field list tblXXX # find field ids for triggers # 2. Create workflow inkreach-teable workflow create --name "My automation" # → returns wflXXX # 3. Set trigger inkreach-teable trigger set wflXXX --type recordUpdated \ --table-id tblXXX --field-ids '["fldStatus"]' # 4. Add action(s) inkreach-teable action add wflXXX --type script \ --code 'return { status: triggerInput.record.fields.Status }' # 5. (Optional) add secrets the script needs inkreach-teable secret set wflXXX --name API_KEY --value sk_xxx # 6. Activate inkreach-teable workflow activate wflXXX # → returns snapshotId # 7. Test inkreach-teable fire button-click tblXXX recXXX fldXXX # for buttonClick # or just perform the triggering action (create/update a record, POST webhook...) inkreach-teable runs list wflXXX ``` ## Editing an Existing Workflow ```bash # Change trigger config inkreach-teable trigger set wflXXX --type buttonClick --table-id tblXXX --field-ids '["fldNew"]' # Add another action inkreach-teable action add wflXXX --type sendEmail --to a@b.com --subject Done --body '

ok

' # Remove an action inkreach-teable action delete wflXXX node-12345 # RE-ACTIVATE to snapshot the changes inkreach-teable workflow activate wflXXX ``` `trigger set` / `action add/update/delete` internally do GET→modify→PUT the whole graph, so they're safe. But you **must re-activate** for changes to take effect at runtime. ## Secrets Scripts often need API keys. Never hardcode — use secrets. ```bash inkreach-teable secret set wflXXX --name OPENAI_KEY --value sk-xxx --description "for AI calls" inkreach-teable secret list wflXXX # names only — value never returned inkreach-teable secret update wflXXX secXXX --value sk-new inkreach-teable secret delete wflXXX secXXX ``` In a script action, read via `process.env.OPENAI_KEY`. Secrets are AES-encrypted at rest. ## Testing / Triggering Most triggers fire from real events (record create/update, form submit, schedule). Two triggers are manually fireable for testing: ```bash # Button field click inkreach-teable fire button-click # Webhook (no auth needed) inkreach-teable fire webhook --body '{"event":"test"}' ``` For other triggers, test by performing the real action (create/update a record, submit the form). Then inspect: ```bash inkreach-teable runs list wflXXX --status failed inkreach-teable runs get wflXXX # rerun a failed execution inkreach-teable runs rerun wflXXX ``` ### `workflow test` vs real fire `workflow test ` runs actions synchronously with **mock input** (a fake `{record:{id:'rec_test',fields:{}}, user:{...}}`). It does NOT fire the trigger or use the queue. Two caveats: - **Upstream outputs are absent in test mode** — if a script reads `triggerInput.record.fields.`, it gets `{}` because the mock record has no fields. Test only validates that the script *runs without throwing*, not that it produces correct data. - For data-dependent validation, do a **real fire** (button-click / webhook) or perform the real triggering action, then check `runs list`. ## Variable References (cross-node data) An action can reference upstream nodes' output in string fields via `{{nodeIndex.path}}`: - `{{1.record.id}}` — trigger record's id (trigger = node 1) - `{{1.record.fields.Status}}` — trigger record's Status field - `{{2.records}}` — output of the first action (node 2) Node index follows **topological execution order** (trigger is always 1). Script actions can also read the merged upstream output directly via `triggerInput`. ## Common Patterns ### Notify on status change ```bash inkreach-teable workflow create --name "Status→email" inkreach-teable trigger set wflXXX --type recordUpdated --table-id tblXXX --field-ids '["fldStatus"]' inkreach-teable action add wflXXX --type sendEmail \ --to ops@x.com --subject 'Status changed' \ --body '{{1.record.fields.Status}}' inkreach-teable workflow activate wflXXX ``` ### Daily digest ```bash inkreach-teable trigger set wflXXX --type scheduledTime \ --schedule '{"timing":{"type":"hours","interval":24}}' ``` ### Button → script → update another table ```bash inkreach-teable trigger set wflXXX --type buttonClick --table-id tblA --field-ids '["fldGo"]' inkreach-teable action add wflXXX --type script --code ' const r = triggerInput.record; // call back into the instance API using the auto-injected token const res = await fetch(triggerInput._apiBaseUrl + "/api/table/tblB/record", { method: "POST", headers: { Authorization: "Bearer " + triggerInput._accessToken, "Content-Type": "application/json" }, body: JSON.stringify({ fields: { Source: r.id } }) }); return { created: await res.json() }; ' ```