@withone/cli 1.16.0 → 1.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-SX2Y4HDW.js → chunk-QW4MBV4F.js} +6 -4
- package/dist/{flow-runner-7RUK2WMF.js → flow-runner-AJGIUT6E.js} +1 -1
- package/dist/index.js +301 -274
- package/package.json +1 -1
- package/skills/one/SKILL.md +116 -0
- package/skills/one/references/flows.md +278 -0
- package/skills/one/references/relay.md +179 -0
- package/skills/one-actions/SKILL.md +0 -157
- package/skills/one-flow/SKILL.md +0 -917
- package/skills/one-relay/SKILL.md +0 -303
package/skills/one-flow/SKILL.md
DELETED
|
@@ -1,917 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: one-flow
|
|
3
|
-
description: |
|
|
4
|
-
Build and execute multi-step API workflows that chain actions across platforms — like n8n/Zapier but file-based. Workflows are JSON files stored at `.one/flows/<key>.flow.json`.
|
|
5
|
-
|
|
6
|
-
TRIGGER when the user wants to:
|
|
7
|
-
- Create a multi-step workflow or automation (e.g., "create a workflow that looks up a customer in Stripe and sends them an email")
|
|
8
|
-
- Chain multiple API actions together across platforms
|
|
9
|
-
- Build a pipeline or sequence of API calls
|
|
10
|
-
- Execute, validate, or manage existing workflows
|
|
11
|
-
- Automate a process involving multiple connected platforms
|
|
12
|
-
- Schedule or orchestrate a series of actions
|
|
13
|
-
|
|
14
|
-
DO NOT TRIGGER for:
|
|
15
|
-
- Single action execution (use one-actions skill instead)
|
|
16
|
-
- Setting up One or installing MCP (that's `one init`)
|
|
17
|
-
- Adding new connections (that's `one connection add`)
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
# One Workflows — Multi-Step API Workflows
|
|
21
|
-
|
|
22
|
-
<!-- Canonical flow schema: src/lib/flow-schema.ts (drives both validation and guide generation) -->
|
|
23
|
-
|
|
24
|
-
You have access to the One CLI's workflow engine, which lets you create and execute multi-step API workflows as JSON files. Workflows chain actions across platforms — e.g., look up a Stripe customer, then send them a welcome email via Gmail.
|
|
25
|
-
|
|
26
|
-
## 1. Overview
|
|
27
|
-
|
|
28
|
-
- Workflows are JSON files stored at `.one/flows/<key>.flow.json`
|
|
29
|
-
- All dynamic values (including connection keys) are declared as **inputs**
|
|
30
|
-
- Each workflow has a unique **key** used to reference and execute it
|
|
31
|
-
- Executed via `one --agent flow execute <key> -i name=value`
|
|
32
|
-
|
|
33
|
-
## 2. Building a Workflow — Step-by-Step Process
|
|
34
|
-
|
|
35
|
-
**You MUST follow this process to build a correct workflow:**
|
|
36
|
-
|
|
37
|
-
### Step 0: Design the workflow
|
|
38
|
-
|
|
39
|
-
Before touching any CLI commands, understand what you are building:
|
|
40
|
-
|
|
41
|
-
1. **Clarify the end goal.** What output does the user actually need? A report? A notification? An enriched dataset? Do not assume — ask if unclear.
|
|
42
|
-
2. **Map the full value chain.** List every step required to deliver that output at production quality. Fetching raw data is never the final step — ask yourself: "If I handed this raw API response to the user, would they be satisfied?" If no, you need analysis or enrichment steps.
|
|
43
|
-
3. **Identify where AI analysis is needed.** Any time raw data needs summarization, scoring, classification, comparison, or natural-language generation, plan a `bash` step using `claude --print`. See the AI-Augmented Patterns section below.
|
|
44
|
-
4. **Write the step sequence as a plain list** before constructing JSON. Example:
|
|
45
|
-
- Fetch competitor pricing from API
|
|
46
|
-
- Write data to temp file
|
|
47
|
-
- Claude analyzes competitive positioning (bash step)
|
|
48
|
-
- Parse Claude's JSON output (code step)
|
|
49
|
-
- Send formatted report via email
|
|
50
|
-
|
|
51
|
-
**Common mistake:** Jumping straight to `one actions search` and building a workflow that only fetches and pipes raw data. The result is a shallow data dump, not a useful workflow. Always design first.
|
|
52
|
-
|
|
53
|
-
### Step 1: Discover connections
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
one --agent connection list
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Find out which platforms are connected and get their connection keys.
|
|
60
|
-
|
|
61
|
-
### Step 2: For EACH API action needed, get the knowledge
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
# Find the action ID
|
|
65
|
-
one --agent actions search <platform> "<query>" -t execute
|
|
66
|
-
|
|
67
|
-
# Read the full docs — REQUIRED before adding to a workflow
|
|
68
|
-
one --agent actions knowledge <platform> <actionId>
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
**CRITICAL:** You MUST call `one actions knowledge` for every action you include in the workflow. The knowledge output tells you the exact request body structure, required fields, path variables, and query parameters. Without this, your workflow JSON will have incorrect data shapes.
|
|
72
|
-
|
|
73
|
-
### Step 3: Construct the workflow JSON
|
|
74
|
-
|
|
75
|
-
Using the knowledge gathered, build the workflow JSON with:
|
|
76
|
-
- All inputs declared (connection keys + user parameters)
|
|
77
|
-
- Each step with the correct actionId, platform, and data structure (from knowledge)
|
|
78
|
-
- Data wired between steps using `$.input.*` and `$.steps.*` selectors
|
|
79
|
-
|
|
80
|
-
### Step 4: Write the workflow file
|
|
81
|
-
|
|
82
|
-
```bash
|
|
83
|
-
one --agent flow create <key> --definition '<json>'
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
Or write directly to `.one/flows/<key>.flow.json`.
|
|
87
|
-
|
|
88
|
-
### Step 5: Validate
|
|
89
|
-
|
|
90
|
-
```bash
|
|
91
|
-
one --agent flow validate <key>
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### Step 6: Execute
|
|
95
|
-
|
|
96
|
-
```bash
|
|
97
|
-
one --agent flow execute <key> -i connectionKey=xxx -i param=value
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## 3. Workflow JSON Schema Reference
|
|
101
|
-
|
|
102
|
-
```json
|
|
103
|
-
{
|
|
104
|
-
"key": "welcome-customer",
|
|
105
|
-
"name": "Welcome New Customer",
|
|
106
|
-
"description": "Look up a Stripe customer and send them a welcome email via Gmail",
|
|
107
|
-
"version": "1",
|
|
108
|
-
"inputs": {
|
|
109
|
-
"stripeConnectionKey": {
|
|
110
|
-
"type": "string",
|
|
111
|
-
"required": true,
|
|
112
|
-
"description": "Stripe connection key from one connection list",
|
|
113
|
-
"connection": { "platform": "stripe" }
|
|
114
|
-
},
|
|
115
|
-
"gmailConnectionKey": {
|
|
116
|
-
"type": "string",
|
|
117
|
-
"required": true,
|
|
118
|
-
"description": "Gmail connection key from one connection list",
|
|
119
|
-
"connection": { "platform": "gmail" }
|
|
120
|
-
},
|
|
121
|
-
"customerEmail": {
|
|
122
|
-
"type": "string",
|
|
123
|
-
"required": true,
|
|
124
|
-
"description": "Customer email to look up"
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
|
-
"steps": [
|
|
128
|
-
{
|
|
129
|
-
"id": "stepId",
|
|
130
|
-
"name": "Human-readable label",
|
|
131
|
-
"type": "action",
|
|
132
|
-
"action": {
|
|
133
|
-
"platform": "stripe",
|
|
134
|
-
"actionId": "the-action-id-from-search",
|
|
135
|
-
"connectionKey": "$.input.stripeConnectionKey",
|
|
136
|
-
"data": {},
|
|
137
|
-
"pathVars": {},
|
|
138
|
-
"queryParams": {},
|
|
139
|
-
"headers": {}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
]
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
### Input declarations
|
|
147
|
-
|
|
148
|
-
| Field | Type | Description |
|
|
149
|
-
|---|---|---|
|
|
150
|
-
| `type` | string | `string`, `number`, `boolean`, `object`, `array` |
|
|
151
|
-
| `required` | boolean | Whether this input must be provided (default: true) |
|
|
152
|
-
| `default` | any | Default value if not provided |
|
|
153
|
-
| `description` | string | Human-readable description |
|
|
154
|
-
| `connection` | object | Connection metadata: `{ "platform": "gmail" }` — enables auto-resolution |
|
|
155
|
-
|
|
156
|
-
**Connection inputs** have a `connection` field. If the user has exactly one connection for that platform, the workflow engine auto-resolves it.
|
|
157
|
-
|
|
158
|
-
## 4. Selector Syntax Reference
|
|
159
|
-
|
|
160
|
-
| Pattern | Resolves To |
|
|
161
|
-
|---|---|
|
|
162
|
-
| `$.input.gmailConnectionKey` | Input value (including connection keys) |
|
|
163
|
-
| `$.input.customerEmail` | Any input parameter |
|
|
164
|
-
| `$.steps.stepId.response` | Full API response from a step |
|
|
165
|
-
| `$.steps.stepId.response.data[0].email` | Nested field with array index |
|
|
166
|
-
| `$.steps.stepId.response.data[*].id` | Wildcard — maps array to field |
|
|
167
|
-
| `$.env.MY_VAR` | Environment variable |
|
|
168
|
-
| `$.loop.item` | Current loop item |
|
|
169
|
-
| `$.loop.i` | Current loop index |
|
|
170
|
-
| `"Hello {{$.steps.getUser.response.data.name}}"` | String interpolation |
|
|
171
|
-
|
|
172
|
-
**Rules:**
|
|
173
|
-
- A value that is purely `$.xxx` resolves to the raw type (object, array, number)
|
|
174
|
-
- A string containing `{{$.xxx}}` does string interpolation (stringifies objects)
|
|
175
|
-
- Selectors inside objects/arrays are resolved recursively
|
|
176
|
-
|
|
177
|
-
## 5. Step Types Reference
|
|
178
|
-
|
|
179
|
-
### `action` — Execute a One API action
|
|
180
|
-
|
|
181
|
-
```json
|
|
182
|
-
{
|
|
183
|
-
"id": "findCustomer",
|
|
184
|
-
"name": "Search Stripe customers",
|
|
185
|
-
"type": "action",
|
|
186
|
-
"action": {
|
|
187
|
-
"platform": "stripe",
|
|
188
|
-
"actionId": "conn_mod_def::xxx::yyy",
|
|
189
|
-
"connectionKey": "$.input.stripeConnectionKey",
|
|
190
|
-
"data": {
|
|
191
|
-
"query": "email:'{{$.input.customerEmail}}'"
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### `transform` — Transform data with a JS expression
|
|
198
|
-
|
|
199
|
-
```json
|
|
200
|
-
{
|
|
201
|
-
"id": "extractNames",
|
|
202
|
-
"name": "Extract customer names",
|
|
203
|
-
"type": "transform",
|
|
204
|
-
"transform": {
|
|
205
|
-
"expression": "$.steps.findCustomer.response.data.map(c => c.name)"
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
The expression is evaluated with the full flow context as `$`.
|
|
211
|
-
|
|
212
|
-
### `code` — Run multi-line JavaScript
|
|
213
|
-
|
|
214
|
-
Unlike `transform` (single expression, implicit return), `code` runs a full function body with explicit `return`. Use it when you need variables, loops, try/catch, or `await`.
|
|
215
|
-
|
|
216
|
-
```json
|
|
217
|
-
{
|
|
218
|
-
"id": "processData",
|
|
219
|
-
"name": "Process and enrich data",
|
|
220
|
-
"type": "code",
|
|
221
|
-
"code": {
|
|
222
|
-
"source": "const customers = $.steps.listCustomers.response.data;\nconst enriched = customers.map(c => ({\n ...c,\n tier: c.spend > 1000 ? 'gold' : 'silver'\n}));\nreturn enriched;"
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
The `source` field contains a JS function body. The flow context is available as `$`. The function is async, so you can use `await`. The return value is stored as the step result.
|
|
228
|
-
|
|
229
|
-
### `condition` — If/then/else branching
|
|
230
|
-
|
|
231
|
-
```json
|
|
232
|
-
{
|
|
233
|
-
"id": "checkFound",
|
|
234
|
-
"name": "Check if customer was found",
|
|
235
|
-
"type": "condition",
|
|
236
|
-
"condition": {
|
|
237
|
-
"expression": "$.steps.findCustomer.response.data.length > 0",
|
|
238
|
-
"then": [
|
|
239
|
-
{ "id": "sendEmail", "name": "Send welcome email", "type": "action", "action": { "..." : "..." } }
|
|
240
|
-
],
|
|
241
|
-
"else": [
|
|
242
|
-
{ "id": "logNotFound", "name": "Log not found", "type": "transform", "transform": { "expression": "'Customer not found'" } }
|
|
243
|
-
]
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
### `loop` — Iterate over an array
|
|
249
|
-
|
|
250
|
-
```json
|
|
251
|
-
{
|
|
252
|
-
"id": "processOrders",
|
|
253
|
-
"name": "Process each order",
|
|
254
|
-
"type": "loop",
|
|
255
|
-
"loop": {
|
|
256
|
-
"over": "$.steps.listOrders.response.data",
|
|
257
|
-
"as": "order",
|
|
258
|
-
"indexAs": "i",
|
|
259
|
-
"maxIterations": 1000,
|
|
260
|
-
"maxConcurrency": 5,
|
|
261
|
-
"steps": [
|
|
262
|
-
{
|
|
263
|
-
"id": "createInvoice",
|
|
264
|
-
"name": "Create invoice for order",
|
|
265
|
-
"type": "action",
|
|
266
|
-
"action": {
|
|
267
|
-
"platform": "quickbooks",
|
|
268
|
-
"actionId": "...",
|
|
269
|
-
"connectionKey": "$.input.qbConnectionKey",
|
|
270
|
-
"data": { "amount": "$.loop.order.total" }
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
]
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
- `maxConcurrency` (optional): When set > 1, loop iterations run in parallel batches of that size. Default is sequential (1).
|
|
279
|
-
|
|
280
|
-
### `parallel` — Run steps concurrently
|
|
281
|
-
|
|
282
|
-
```json
|
|
283
|
-
{
|
|
284
|
-
"id": "parallelLookups",
|
|
285
|
-
"name": "Look up in parallel",
|
|
286
|
-
"type": "parallel",
|
|
287
|
-
"parallel": {
|
|
288
|
-
"maxConcurrency": 5,
|
|
289
|
-
"steps": [
|
|
290
|
-
{ "id": "getStripe", "name": "Get Stripe data", "type": "action", "action": { "...": "..." } },
|
|
291
|
-
{ "id": "getHubspot", "name": "Get HubSpot data", "type": "action", "action": { "...": "..." } }
|
|
292
|
-
]
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
### `file-read` — Read from filesystem
|
|
298
|
-
|
|
299
|
-
```json
|
|
300
|
-
{
|
|
301
|
-
"id": "readConfig",
|
|
302
|
-
"name": "Read config file",
|
|
303
|
-
"type": "file-read",
|
|
304
|
-
"fileRead": { "path": "./data/config.json", "parseJson": true }
|
|
305
|
-
}
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
### `file-write` — Write to filesystem
|
|
309
|
-
|
|
310
|
-
```json
|
|
311
|
-
{
|
|
312
|
-
"id": "writeResults",
|
|
313
|
-
"name": "Save results",
|
|
314
|
-
"type": "file-write",
|
|
315
|
-
"fileWrite": {
|
|
316
|
-
"path": "./output/results.json",
|
|
317
|
-
"content": "$.steps.transform.output",
|
|
318
|
-
"append": false
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
### `while` — Condition-driven loop (do-while)
|
|
324
|
-
|
|
325
|
-
Iterates until a condition becomes falsy. The first iteration always runs (do-while semantics), then the condition is checked before each subsequent iteration. Useful for pagination.
|
|
326
|
-
|
|
327
|
-
```json
|
|
328
|
-
{
|
|
329
|
-
"id": "paginate",
|
|
330
|
-
"name": "Paginate through all pages",
|
|
331
|
-
"type": "while",
|
|
332
|
-
"while": {
|
|
333
|
-
"condition": "$.steps.paginate.output.lastResult.nextPageToken != null",
|
|
334
|
-
"maxIterations": 50,
|
|
335
|
-
"steps": [
|
|
336
|
-
{
|
|
337
|
-
"id": "fetchPage",
|
|
338
|
-
"name": "Fetch next page",
|
|
339
|
-
"type": "action",
|
|
340
|
-
"action": {
|
|
341
|
-
"platform": "gmail",
|
|
342
|
-
"actionId": "GMAIL_LIST_MESSAGES_ACTION_ID",
|
|
343
|
-
"connectionKey": "$.input.gmailKey",
|
|
344
|
-
"queryParams": {
|
|
345
|
-
"pageToken": "$.steps.paginate.output.lastResult.nextPageToken"
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
]
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
| Field | Type | Description |
|
|
355
|
-
|---|---|---|
|
|
356
|
-
| `condition` | string | JS expression evaluated before each iteration (after iteration 0) |
|
|
357
|
-
| `maxIterations` | number | Safety cap, default: 100 |
|
|
358
|
-
| `steps` | FlowStep[] | Steps to execute each iteration |
|
|
359
|
-
|
|
360
|
-
The step output contains `lastResult` (last step's output from most recent iteration), `iteration` (count), and `results` (array of all iteration outputs). Reference via `$.steps.<id>.output.lastResult`.
|
|
361
|
-
|
|
362
|
-
### `flow` — Execute a sub-flow
|
|
363
|
-
|
|
364
|
-
Loads and executes another saved flow, enabling flow composition. Circular flows are detected and blocked.
|
|
365
|
-
|
|
366
|
-
```json
|
|
367
|
-
{
|
|
368
|
-
"id": "processCustomer",
|
|
369
|
-
"name": "Run customer enrichment flow",
|
|
370
|
-
"type": "flow",
|
|
371
|
-
"flow": {
|
|
372
|
-
"key": "enrich-customer",
|
|
373
|
-
"inputs": {
|
|
374
|
-
"email": "$.steps.getCustomer.response.email",
|
|
375
|
-
"connectionKey": "$.input.hubspotConnectionKey"
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
| Field | Type | Description |
|
|
382
|
-
|---|---|---|
|
|
383
|
-
| `key` | string | Flow key or path (supports selectors) |
|
|
384
|
-
| `inputs` | object | Input values mapped to the sub-flow's declared inputs (supports selectors) |
|
|
385
|
-
|
|
386
|
-
The step output contains all sub-flow step results. The full sub-flow context is available via `$.steps.<id>.response`.
|
|
387
|
-
|
|
388
|
-
### `paginate` — Auto-collect paginated API results
|
|
389
|
-
|
|
390
|
-
Automatically pages through a paginated API, collecting all results into a single array.
|
|
391
|
-
|
|
392
|
-
```json
|
|
393
|
-
{
|
|
394
|
-
"id": "allMessages",
|
|
395
|
-
"name": "Fetch all Gmail messages",
|
|
396
|
-
"type": "paginate",
|
|
397
|
-
"paginate": {
|
|
398
|
-
"action": {
|
|
399
|
-
"platform": "gmail",
|
|
400
|
-
"actionId": "GMAIL_LIST_MESSAGES_ACTION_ID",
|
|
401
|
-
"connectionKey": "$.input.gmailKey",
|
|
402
|
-
"queryParams": { "maxResults": 100 }
|
|
403
|
-
},
|
|
404
|
-
"pageTokenField": "nextPageToken",
|
|
405
|
-
"resultsField": "messages",
|
|
406
|
-
"inputTokenParam": "queryParams.pageToken",
|
|
407
|
-
"maxPages": 10
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
| Field | Type | Description |
|
|
413
|
-
|---|---|---|
|
|
414
|
-
| `action` | FlowActionConfig | The API action to call (same format as action steps) |
|
|
415
|
-
| `pageTokenField` | string | Dot-path in the API response to the next page token |
|
|
416
|
-
| `resultsField` | string | Dot-path in the API response to the results array |
|
|
417
|
-
| `inputTokenParam` | string | Dot-path in the action config where the page token is injected |
|
|
418
|
-
| `maxPages` | number | Maximum pages to fetch, default: 10 |
|
|
419
|
-
|
|
420
|
-
Output is the concatenated results array. Response includes `{ pages, totalResults, results }`.
|
|
421
|
-
|
|
422
|
-
### `bash` — Execute shell commands
|
|
423
|
-
|
|
424
|
-
Runs a shell command. **Requires `--allow-bash` flag** for security.
|
|
425
|
-
|
|
426
|
-
```json
|
|
427
|
-
{
|
|
428
|
-
"id": "analyzeData",
|
|
429
|
-
"name": "Analyze data with Claude",
|
|
430
|
-
"type": "bash",
|
|
431
|
-
"bash": {
|
|
432
|
-
"command": "claude --print 'Analyze: {{$.steps.fetchData.response}}' --output-format json",
|
|
433
|
-
"timeout": 180000,
|
|
434
|
-
"parseJson": true
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
| Field | Type | Description |
|
|
440
|
-
|---|---|---|
|
|
441
|
-
| `command` | string | Shell command to execute (supports selectors and interpolation) |
|
|
442
|
-
| `timeout` | number | Timeout in ms, default: 30000 |
|
|
443
|
-
| `parseJson` | boolean | Parse stdout as JSON, default: false |
|
|
444
|
-
| `cwd` | string | Working directory (supports selectors) |
|
|
445
|
-
| `env` | object | Additional environment variables |
|
|
446
|
-
|
|
447
|
-
Output is stdout (trimmed, or parsed as JSON if `parseJson` is true). Response includes `{ stdout, stderr, exitCode }`.
|
|
448
|
-
|
|
449
|
-
**Security:** Bash steps are blocked by default. Pass `--allow-bash` to `one flow execute` to enable them.
|
|
450
|
-
|
|
451
|
-
## 6. Error Handling
|
|
452
|
-
|
|
453
|
-
### `onError` strategies
|
|
454
|
-
|
|
455
|
-
```json
|
|
456
|
-
{
|
|
457
|
-
"id": "riskyStep",
|
|
458
|
-
"name": "Might fail",
|
|
459
|
-
"type": "action",
|
|
460
|
-
"onError": {
|
|
461
|
-
"strategy": "retry",
|
|
462
|
-
"retries": 3,
|
|
463
|
-
"retryDelayMs": 1000
|
|
464
|
-
},
|
|
465
|
-
"action": { "...": "..." }
|
|
466
|
-
}
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
| Strategy | Behavior |
|
|
470
|
-
|---|---|
|
|
471
|
-
| `fail` | Stop the flow immediately (default) |
|
|
472
|
-
| `continue` | Mark step as failed, continue to next step |
|
|
473
|
-
| `retry` | Retry up to N times with delay |
|
|
474
|
-
| `fallback` | On failure, execute a different step |
|
|
475
|
-
|
|
476
|
-
### Conditional execution
|
|
477
|
-
|
|
478
|
-
Skip a step based on previous results:
|
|
479
|
-
|
|
480
|
-
```json
|
|
481
|
-
{
|
|
482
|
-
"id": "sendEmail",
|
|
483
|
-
"name": "Send email only if customer found",
|
|
484
|
-
"type": "action",
|
|
485
|
-
"if": "$.steps.findCustomer.response.data.length > 0",
|
|
486
|
-
"action": { "...": "..." }
|
|
487
|
-
}
|
|
488
|
-
```
|
|
489
|
-
|
|
490
|
-
## 7. Updating Existing Workflows
|
|
491
|
-
|
|
492
|
-
To modify an existing workflow:
|
|
493
|
-
|
|
494
|
-
1. Read the workflow JSON file at `.one/flows/<key>.flow.json`
|
|
495
|
-
2. Understand its current structure
|
|
496
|
-
3. Use `one --agent actions knowledge <platform> <actionId>` for any new actions
|
|
497
|
-
4. Modify the JSON (add/remove/update steps, change data mappings, add inputs)
|
|
498
|
-
5. Write back the updated workflow file
|
|
499
|
-
6. Validate: `one --agent flow validate <key>`
|
|
500
|
-
|
|
501
|
-
## 8. Complete Examples
|
|
502
|
-
|
|
503
|
-
### Example 1: Simple 2-step — Search Stripe customer, send Gmail email
|
|
504
|
-
|
|
505
|
-
```json
|
|
506
|
-
{
|
|
507
|
-
"key": "welcome-customer",
|
|
508
|
-
"name": "Welcome New Customer",
|
|
509
|
-
"description": "Look up a Stripe customer and send them a welcome email",
|
|
510
|
-
"version": "1",
|
|
511
|
-
"inputs": {
|
|
512
|
-
"stripeConnectionKey": {
|
|
513
|
-
"type": "string",
|
|
514
|
-
"required": true,
|
|
515
|
-
"description": "Stripe connection key",
|
|
516
|
-
"connection": { "platform": "stripe" }
|
|
517
|
-
},
|
|
518
|
-
"gmailConnectionKey": {
|
|
519
|
-
"type": "string",
|
|
520
|
-
"required": true,
|
|
521
|
-
"description": "Gmail connection key",
|
|
522
|
-
"connection": { "platform": "gmail" }
|
|
523
|
-
},
|
|
524
|
-
"customerEmail": {
|
|
525
|
-
"type": "string",
|
|
526
|
-
"required": true,
|
|
527
|
-
"description": "Customer email to look up"
|
|
528
|
-
}
|
|
529
|
-
},
|
|
530
|
-
"steps": [
|
|
531
|
-
{
|
|
532
|
-
"id": "findCustomer",
|
|
533
|
-
"name": "Search for customer in Stripe",
|
|
534
|
-
"type": "action",
|
|
535
|
-
"action": {
|
|
536
|
-
"platform": "stripe",
|
|
537
|
-
"actionId": "STRIPE_SEARCH_CUSTOMERS_ACTION_ID",
|
|
538
|
-
"connectionKey": "$.input.stripeConnectionKey",
|
|
539
|
-
"data": {
|
|
540
|
-
"query": "email:'{{$.input.customerEmail}}'"
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
},
|
|
544
|
-
{
|
|
545
|
-
"id": "sendWelcome",
|
|
546
|
-
"name": "Send welcome email via Gmail",
|
|
547
|
-
"type": "action",
|
|
548
|
-
"if": "$.steps.findCustomer.response.data && $.steps.findCustomer.response.data.length > 0",
|
|
549
|
-
"action": {
|
|
550
|
-
"platform": "gmail",
|
|
551
|
-
"actionId": "GMAIL_SEND_EMAIL_ACTION_ID",
|
|
552
|
-
"connectionKey": "$.input.gmailConnectionKey",
|
|
553
|
-
"data": {
|
|
554
|
-
"to": "{{$.input.customerEmail}}",
|
|
555
|
-
"subject": "Welcome, {{$.steps.findCustomer.response.data[0].name}}!",
|
|
556
|
-
"body": "Thank you for being a customer. We're glad to have you!"
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
]
|
|
561
|
-
}
|
|
562
|
-
```
|
|
563
|
-
|
|
564
|
-
### Example 2: Conditional — Check if HubSpot contact exists, create or update
|
|
565
|
-
|
|
566
|
-
```json
|
|
567
|
-
{
|
|
568
|
-
"key": "sync-hubspot-contact",
|
|
569
|
-
"name": "Sync Contact to HubSpot",
|
|
570
|
-
"description": "Check if a contact exists in HubSpot, create if new or update if existing",
|
|
571
|
-
"version": "1",
|
|
572
|
-
"inputs": {
|
|
573
|
-
"hubspotConnectionKey": {
|
|
574
|
-
"type": "string",
|
|
575
|
-
"required": true,
|
|
576
|
-
"connection": { "platform": "hub-spot" }
|
|
577
|
-
},
|
|
578
|
-
"email": { "type": "string", "required": true },
|
|
579
|
-
"firstName": { "type": "string", "required": true },
|
|
580
|
-
"lastName": { "type": "string", "required": true }
|
|
581
|
-
},
|
|
582
|
-
"steps": [
|
|
583
|
-
{
|
|
584
|
-
"id": "searchContact",
|
|
585
|
-
"name": "Search for existing contact",
|
|
586
|
-
"type": "action",
|
|
587
|
-
"action": {
|
|
588
|
-
"platform": "hub-spot",
|
|
589
|
-
"actionId": "HUBSPOT_SEARCH_CONTACTS_ACTION_ID",
|
|
590
|
-
"connectionKey": "$.input.hubspotConnectionKey",
|
|
591
|
-
"data": {
|
|
592
|
-
"filterGroups": [{ "filters": [{ "propertyName": "email", "operator": "EQ", "value": "$.input.email" }] }]
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
},
|
|
596
|
-
{
|
|
597
|
-
"id": "createOrUpdate",
|
|
598
|
-
"name": "Create or update contact",
|
|
599
|
-
"type": "condition",
|
|
600
|
-
"condition": {
|
|
601
|
-
"expression": "$.steps.searchContact.response.total > 0",
|
|
602
|
-
"then": [
|
|
603
|
-
{
|
|
604
|
-
"id": "updateContact",
|
|
605
|
-
"name": "Update existing contact",
|
|
606
|
-
"type": "action",
|
|
607
|
-
"action": {
|
|
608
|
-
"platform": "hub-spot",
|
|
609
|
-
"actionId": "HUBSPOT_UPDATE_CONTACT_ACTION_ID",
|
|
610
|
-
"connectionKey": "$.input.hubspotConnectionKey",
|
|
611
|
-
"pathVars": { "contactId": "$.steps.searchContact.response.results[0].id" },
|
|
612
|
-
"data": {
|
|
613
|
-
"properties": { "firstname": "$.input.firstName", "lastname": "$.input.lastName" }
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
],
|
|
618
|
-
"else": [
|
|
619
|
-
{
|
|
620
|
-
"id": "createContact",
|
|
621
|
-
"name": "Create new contact",
|
|
622
|
-
"type": "action",
|
|
623
|
-
"action": {
|
|
624
|
-
"platform": "hub-spot",
|
|
625
|
-
"actionId": "HUBSPOT_CREATE_CONTACT_ACTION_ID",
|
|
626
|
-
"connectionKey": "$.input.hubspotConnectionKey",
|
|
627
|
-
"data": {
|
|
628
|
-
"properties": { "email": "$.input.email", "firstname": "$.input.firstName", "lastname": "$.input.lastName" }
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
]
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
]
|
|
636
|
-
}
|
|
637
|
-
```
|
|
638
|
-
|
|
639
|
-
### Example 3: Loop — Iterate over Shopify orders, create invoices
|
|
640
|
-
|
|
641
|
-
```json
|
|
642
|
-
{
|
|
643
|
-
"key": "shopify-to-invoices",
|
|
644
|
-
"name": "Shopify Orders to Invoices",
|
|
645
|
-
"description": "Fetch recent Shopify orders and create an invoice for each",
|
|
646
|
-
"version": "1",
|
|
647
|
-
"inputs": {
|
|
648
|
-
"shopifyConnectionKey": {
|
|
649
|
-
"type": "string",
|
|
650
|
-
"required": true,
|
|
651
|
-
"connection": { "platform": "shopify" }
|
|
652
|
-
},
|
|
653
|
-
"qbConnectionKey": {
|
|
654
|
-
"type": "string",
|
|
655
|
-
"required": true,
|
|
656
|
-
"connection": { "platform": "quick-books" }
|
|
657
|
-
}
|
|
658
|
-
},
|
|
659
|
-
"steps": [
|
|
660
|
-
{
|
|
661
|
-
"id": "listOrders",
|
|
662
|
-
"name": "List recent Shopify orders",
|
|
663
|
-
"type": "action",
|
|
664
|
-
"action": {
|
|
665
|
-
"platform": "shopify",
|
|
666
|
-
"actionId": "SHOPIFY_LIST_ORDERS_ACTION_ID",
|
|
667
|
-
"connectionKey": "$.input.shopifyConnectionKey",
|
|
668
|
-
"queryParams": { "status": "any", "limit": "50" }
|
|
669
|
-
}
|
|
670
|
-
},
|
|
671
|
-
{
|
|
672
|
-
"id": "createInvoices",
|
|
673
|
-
"name": "Create invoice for each order",
|
|
674
|
-
"type": "loop",
|
|
675
|
-
"loop": {
|
|
676
|
-
"over": "$.steps.listOrders.response.orders",
|
|
677
|
-
"as": "order",
|
|
678
|
-
"indexAs": "i",
|
|
679
|
-
"steps": [
|
|
680
|
-
{
|
|
681
|
-
"id": "createInvoice",
|
|
682
|
-
"name": "Create QuickBooks invoice",
|
|
683
|
-
"type": "action",
|
|
684
|
-
"onError": { "strategy": "continue" },
|
|
685
|
-
"action": {
|
|
686
|
-
"platform": "quick-books",
|
|
687
|
-
"actionId": "QB_CREATE_INVOICE_ACTION_ID",
|
|
688
|
-
"connectionKey": "$.input.qbConnectionKey",
|
|
689
|
-
"data": {
|
|
690
|
-
"Line": [
|
|
691
|
-
{
|
|
692
|
-
"Amount": "$.loop.order.total_price",
|
|
693
|
-
"Description": "Shopify Order #{{$.loop.order.order_number}}"
|
|
694
|
-
}
|
|
695
|
-
]
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
]
|
|
700
|
-
}
|
|
701
|
-
},
|
|
702
|
-
{
|
|
703
|
-
"id": "summary",
|
|
704
|
-
"name": "Generate summary",
|
|
705
|
-
"type": "transform",
|
|
706
|
-
"transform": {
|
|
707
|
-
"expression": "({ totalOrders: $.steps.listOrders.response.orders.length, processed: $.steps.createInvoices.output.length })"
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
]
|
|
711
|
-
}
|
|
712
|
-
```
|
|
713
|
-
|
|
714
|
-
### Example 4: AI-Augmented — Fetch CRM data, analyze with Claude, email report
|
|
715
|
-
|
|
716
|
-
This example demonstrates the **file-write → bash → code** pattern. Instead of just piping raw data, it uses Claude to perform competitive analysis and delivers an actionable report.
|
|
717
|
-
|
|
718
|
-
```json
|
|
719
|
-
{
|
|
720
|
-
"key": "competitor-analysis",
|
|
721
|
-
"name": "AI Competitor Analysis",
|
|
722
|
-
"description": "Fetch deals from HubSpot, analyze competitive landscape with Claude, email the report",
|
|
723
|
-
"version": "1",
|
|
724
|
-
"inputs": {
|
|
725
|
-
"hubspotConnectionKey": {
|
|
726
|
-
"type": "string",
|
|
727
|
-
"required": true,
|
|
728
|
-
"connection": { "platform": "hub-spot" }
|
|
729
|
-
},
|
|
730
|
-
"gmailConnectionKey": {
|
|
731
|
-
"type": "string",
|
|
732
|
-
"required": true,
|
|
733
|
-
"connection": { "platform": "gmail" }
|
|
734
|
-
},
|
|
735
|
-
"reportEmail": {
|
|
736
|
-
"type": "string",
|
|
737
|
-
"required": true,
|
|
738
|
-
"description": "Email address to send the analysis report to"
|
|
739
|
-
}
|
|
740
|
-
},
|
|
741
|
-
"steps": [
|
|
742
|
-
{
|
|
743
|
-
"id": "fetchDeals",
|
|
744
|
-
"name": "Fetch recent deals from HubSpot",
|
|
745
|
-
"type": "action",
|
|
746
|
-
"action": {
|
|
747
|
-
"platform": "hub-spot",
|
|
748
|
-
"actionId": "HUBSPOT_LIST_DEALS_ACTION_ID",
|
|
749
|
-
"connectionKey": "$.input.hubspotConnectionKey",
|
|
750
|
-
"queryParams": { "limit": "100" }
|
|
751
|
-
}
|
|
752
|
-
},
|
|
753
|
-
{
|
|
754
|
-
"id": "writeDeals",
|
|
755
|
-
"name": "Write deals data for Claude analysis",
|
|
756
|
-
"type": "file-write",
|
|
757
|
-
"fileWrite": {
|
|
758
|
-
"path": "/tmp/competitor-analysis-deals.json",
|
|
759
|
-
"content": "$.steps.fetchDeals.response"
|
|
760
|
-
}
|
|
761
|
-
},
|
|
762
|
-
{
|
|
763
|
-
"id": "analyzeCompetitors",
|
|
764
|
-
"name": "Claude analyzes competitive landscape",
|
|
765
|
-
"type": "bash",
|
|
766
|
-
"bash": {
|
|
767
|
-
"command": "cat /tmp/competitor-analysis-deals.json | claude --print 'You are a competitive intelligence analyst. Analyze these CRM deals and return a JSON object with: {\"totalDeals\": number, \"competitorMentions\": [{\"competitor\": \"name\", \"count\": number, \"winRate\": number, \"commonObjections\": [\"...\"]}], \"summary\": \"2-3 paragraph executive summary\", \"recommendations\": [\"actionable items\"]}. Return ONLY valid JSON.' --output-format json",
|
|
768
|
-
"timeout": 180000,
|
|
769
|
-
"parseJson": true
|
|
770
|
-
}
|
|
771
|
-
},
|
|
772
|
-
{
|
|
773
|
-
"id": "formatReport",
|
|
774
|
-
"name": "Format analysis into email body",
|
|
775
|
-
"type": "code",
|
|
776
|
-
"code": {
|
|
777
|
-
"source": "const a = $.steps.analyzeCompetitors.output;\nconst competitors = a.competitorMentions.map(c => `- ${c.competitor}: ${c.count} mentions, ${c.winRate}% win rate. Objections: ${c.commonObjections.join(', ')}`).join('\\n');\nreturn {\n subject: `Competitive Analysis — ${a.totalDeals} deals analyzed`,\n body: `${a.summary}\\n\\nCompetitor Breakdown:\\n${competitors}\\n\\nRecommendations:\\n${a.recommendations.map((r, i) => `${i+1}. ${r}`).join('\\n')}`\n};"
|
|
778
|
-
}
|
|
779
|
-
},
|
|
780
|
-
{
|
|
781
|
-
"id": "sendReport",
|
|
782
|
-
"name": "Email the analysis report",
|
|
783
|
-
"type": "action",
|
|
784
|
-
"action": {
|
|
785
|
-
"platform": "gmail",
|
|
786
|
-
"actionId": "GMAIL_SEND_EMAIL_ACTION_ID",
|
|
787
|
-
"connectionKey": "$.input.gmailConnectionKey",
|
|
788
|
-
"data": {
|
|
789
|
-
"to": "{{$.input.reportEmail}}",
|
|
790
|
-
"subject": "{{$.steps.formatReport.output.subject}}",
|
|
791
|
-
"body": "{{$.steps.formatReport.output.body}}"
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
]
|
|
796
|
-
}
|
|
797
|
-
```
|
|
798
|
-
|
|
799
|
-
Execute with:
|
|
800
|
-
```bash
|
|
801
|
-
one --agent flow execute competitor-analysis --allow-bash -i reportEmail=team@company.com
|
|
802
|
-
```
|
|
803
|
-
|
|
804
|
-
## 9. AI-Augmented Workflow Patterns
|
|
805
|
-
|
|
806
|
-
Use this pattern whenever raw API data needs analysis, summarization, scoring, classification, or natural-language generation. This is the difference between a shallow data pipe and a workflow that delivers real value.
|
|
807
|
-
|
|
808
|
-
### The file-write → bash → code pattern
|
|
809
|
-
|
|
810
|
-
**Step A: `file-write`** — Write raw data to a temp file. API responses are often too large to inline into a shell command.
|
|
811
|
-
|
|
812
|
-
```json
|
|
813
|
-
{
|
|
814
|
-
"id": "writeData",
|
|
815
|
-
"name": "Write raw data for analysis",
|
|
816
|
-
"type": "file-write",
|
|
817
|
-
"fileWrite": {
|
|
818
|
-
"path": "/tmp/{{$.input.flowKey}}-data.json",
|
|
819
|
-
"content": "$.steps.fetchData.response"
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
```
|
|
823
|
-
|
|
824
|
-
**Step B: `bash`** — Call `claude --print` to analyze the data. This is where intelligence happens.
|
|
825
|
-
|
|
826
|
-
```json
|
|
827
|
-
{
|
|
828
|
-
"id": "analyze",
|
|
829
|
-
"name": "AI analysis",
|
|
830
|
-
"type": "bash",
|
|
831
|
-
"bash": {
|
|
832
|
-
"command": "cat /tmp/{{$.input.flowKey}}-data.json | claude --print 'You are a [domain] analyst. Analyze this data and return JSON with: {\"summary\": \"...\", \"insights\": [...], \"score\": 0-100, \"recommendations\": [...]}. Return ONLY valid JSON, no markdown.' --output-format json",
|
|
833
|
-
"timeout": 180000,
|
|
834
|
-
"parseJson": true
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
```
|
|
838
|
-
|
|
839
|
-
**Step C: `code`** — Parse and structure the AI output for downstream steps.
|
|
840
|
-
|
|
841
|
-
```json
|
|
842
|
-
{
|
|
843
|
-
"id": "formatResult",
|
|
844
|
-
"name": "Structure analysis for output",
|
|
845
|
-
"type": "code",
|
|
846
|
-
"code": {
|
|
847
|
-
"source": "const analysis = $.steps.analyze.output;\nreturn {\n report: `Summary: ${analysis.summary}\\n\\nInsights:\\n${analysis.insights.map((insight, i) => `${i+1}. ${insight}`).join('\\n')}`,\n score: analysis.score\n};"
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
```
|
|
851
|
-
|
|
852
|
-
### When to use this pattern
|
|
853
|
-
|
|
854
|
-
- **Use it** when the user expects analysis, not raw data (e.g., "analyze my competitors", "qualify these leads", "summarize these reviews")
|
|
855
|
-
- **Use it** when data from one API needs intelligent transformation before being sent to another (e.g., generating a personalized email based on CRM data)
|
|
856
|
-
- **Don't use it** for simple field mapping or filtering — use `transform` or `code` steps instead
|
|
857
|
-
|
|
858
|
-
### Prompt engineering tips for bash steps
|
|
859
|
-
|
|
860
|
-
- **Request JSON output** so downstream code steps can parse it — include `Return ONLY valid JSON, no markdown.` in the prompt and use `--output-format json`
|
|
861
|
-
- **Be specific about the analysis** — "Score each lead 0-100 based on company size, role seniority, and engagement recency" beats "analyze these leads"
|
|
862
|
-
- **Include domain context** — "You are a B2B sales analyst" produces better results than a generic prompt
|
|
863
|
-
- **Keep prompts focused** — one analysis task per bash step; chain multiple bash steps for multi-stage analysis
|
|
864
|
-
|
|
865
|
-
### Concurrency and timeout guidance
|
|
866
|
-
|
|
867
|
-
- **Always set `timeout` to at least `180000` (3 minutes)** for bash steps calling `claude --print`. The default 30s bash timeout will fail on nearly all AI analysis tasks. Claude typically needs 60-90s, and under resource contention this can double.
|
|
868
|
-
- **Run Claude-heavy flows sequentially, not in parallel.** Each `claude --print` spawns a separate process. Running multiple flows with bash+Claude steps concurrently causes resource contention and timeout failures — even when individual prompts are small. If orchestrating multiple AI workflows, execute them one at a time.
|
|
869
|
-
- **If a bash+Claude step times out**, the cause is almost always the timeout value or concurrent execution — not prompt size. Increase the timeout and ensure no other Claude-heavy flows are running before assuming the prompt needs to be reduced.
|
|
870
|
-
|
|
871
|
-
## 10. CLI Commands Reference
|
|
872
|
-
|
|
873
|
-
```bash
|
|
874
|
-
# Create a workflow
|
|
875
|
-
one --agent flow create <key> --definition '<json>'
|
|
876
|
-
|
|
877
|
-
# List all workflows
|
|
878
|
-
one --agent flow list
|
|
879
|
-
|
|
880
|
-
# Validate a workflow
|
|
881
|
-
one --agent flow validate <key>
|
|
882
|
-
|
|
883
|
-
# Execute a workflow
|
|
884
|
-
one --agent flow execute <key> -i connectionKey=value -i param=value
|
|
885
|
-
|
|
886
|
-
# Execute with dry run (validate only)
|
|
887
|
-
one --agent flow execute <key> --dry-run -i connectionKey=value
|
|
888
|
-
|
|
889
|
-
# Execute with mock mode (dry-run + mock API responses, runs transforms/code normally)
|
|
890
|
-
one --agent flow execute <key> --dry-run --mock -i connectionKey=value
|
|
891
|
-
|
|
892
|
-
# Execute with bash steps enabled
|
|
893
|
-
one --agent flow execute <key> --allow-bash -i connectionKey=value
|
|
894
|
-
|
|
895
|
-
# Execute with verbose output
|
|
896
|
-
one --agent flow execute <key> -v -i connectionKey=value
|
|
897
|
-
|
|
898
|
-
# List workflow runs
|
|
899
|
-
one --agent flow runs [flowKey]
|
|
900
|
-
|
|
901
|
-
# Resume a paused/failed run
|
|
902
|
-
one --agent flow resume <runId>
|
|
903
|
-
```
|
|
904
|
-
|
|
905
|
-
## Important Notes
|
|
906
|
-
|
|
907
|
-
- **Always use `--agent` flag** for structured JSON output
|
|
908
|
-
- **Always call `one actions knowledge`** before adding an action step to a workflow
|
|
909
|
-
- Platform names are **kebab-case** (e.g., `hub-spot`, not `HubSpot`)
|
|
910
|
-
- Connection keys are **inputs**, not hardcoded — makes workflows portable and shareable
|
|
911
|
-
- Use `$.input.*` for input values, `$.steps.*` for step results
|
|
912
|
-
- Action IDs in examples (like `STRIPE_SEARCH_CUSTOMERS_ACTION_ID`) are placeholders — always use `one actions search` to find the real IDs
|
|
913
|
-
- **Parallel step outputs** are accessible both by index (`$.steps.parallelStep.output[0]`) and by substep ID (`$.steps.substepId.response`)
|
|
914
|
-
- **Loop step outputs** include iteration details via `$.steps.myLoop.response.iterations[0].innerStepId.response`
|
|
915
|
-
- **Code steps** support `await require('crypto')`, `await require('buffer')`, `await require('url')`, `await require('path')` — `fs`, `http`, `child_process`, etc. are blocked
|
|
916
|
-
- **Bash steps** require `--allow-bash` flag for security
|
|
917
|
-
- **State is persisted** after every step completion — resume picks up where it left off
|