@unstable-dev/unmeshed-mcp 0.1.0
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/README.md +64 -0
- package/dist/auth.d.ts +6 -0
- package/dist/auth.js +11 -0
- package/dist/client.d.ts +46 -0
- package/dist/client.js +97 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.js +31 -0
- package/dist/get-docs.d.ts +8 -0
- package/dist/get-docs.js +64 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +35 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.js +203 -0
- package/knowledge/README.md +16 -0
- package/knowledge/SKILL.md +359 -0
- package/knowledge/assets/patterns.md +637 -0
- package/knowledge/execution/debugging-guide.md +18 -0
- package/knowledge/execution/process-run.schema.md +24 -0
- package/knowledge/execution/step-run.schema.md +21 -0
- package/knowledge/process-definition.schema.md +36 -0
- package/knowledge/references/integrations.md +914 -0
- package/knowledge/references/steps-knowledge.md +834 -0
- package/knowledge/step-definition.schema.md +140 -0
- package/knowledge/step-output-paths.md +45 -0
- package/knowledge/steps/DECISION_ENGINE.md +248 -0
- package/knowledge/steps/DEPENDSON.md +296 -0
- package/knowledge/steps/EXIT.md +220 -0
- package/knowledge/steps/FAIL.md +198 -0
- package/knowledge/steps/FLOW_GATEWAY.md +405 -0
- package/knowledge/steps/FOREACH.md +250 -0
- package/knowledge/steps/HTTP.md +183 -0
- package/knowledge/steps/JAVASCRIPT.md +192 -0
- package/knowledge/steps/JQ.md +189 -0
- package/knowledge/steps/LIST.md +279 -0
- package/knowledge/steps/NOOP.md +165 -0
- package/knowledge/steps/PARALLEL.md +366 -0
- package/knowledge/steps/PYTHON.md +206 -0
- package/knowledge/steps/SEND_RESPONSE.md +301 -0
- package/knowledge/steps/SQLITE.md +301 -0
- package/knowledge/steps/SUB_PROCESS.md +296 -0
- package/knowledge/steps/SWITCH.md +369 -0
- package/knowledge/steps/UPDATE_STEP.md +257 -0
- package/knowledge/steps/WAIT.md +218 -0
- package/knowledge/steps/WHILE.md +328 -0
- package/knowledge/steps/WORKER.md +233 -0
- package/knowledge/system-prompt.md +274 -0
- package/package.json +39 -0
|
@@ -0,0 +1,914 @@
|
|
|
1
|
+
# Unmeshed Integrations — Input Schemas
|
|
2
|
+
|
|
3
|
+
All integrations use `"type": "INTEGRATION"` as the step type.
|
|
4
|
+
The `input.type` field selects the specific integration.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## LLM — Claude (Anthropic)
|
|
9
|
+
|
|
10
|
+
**`input.type`**: `"llm-claude"`
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
{
|
|
14
|
+
"type": "llm-claude",
|
|
15
|
+
"name": "unmeshed",
|
|
16
|
+
"publishProperties": {
|
|
17
|
+
"allowedTokens": 500,
|
|
18
|
+
"temperature": 0.1
|
|
19
|
+
},
|
|
20
|
+
"messageBody": {
|
|
21
|
+
"systemPrompt": "You are a ... Return only valid JSON. Do not include markdown or explanation.",
|
|
22
|
+
"userPrompt": "Your prompt here with {{steps.<ref>.output.result.<field>}} references.\n\nReturn JSON in this exact format:\n{ ... }"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Rules:**
|
|
28
|
+
- Always instruct the model to return only JSON in the system prompt when structured output is needed
|
|
29
|
+
- `allowedTokens`: max tokens — can be a number `500` or a string `"100"` — both are valid
|
|
30
|
+
- `temperature`: 0.0–0.1 for deterministic/classification tasks; 0.5–0.9 for generative/creative
|
|
31
|
+
- Output accessed via `steps.<ref>.output.results.<field>` (note: `.results` not `.result`)
|
|
32
|
+
- Always defensively access both: `steps.<ref>.output.results || steps.<ref>.output.result || {}`
|
|
33
|
+
- The `name` field references the configured Claude connection name in Unmeshed (typically `"unmeshed"`)
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## MongoDB
|
|
38
|
+
|
|
39
|
+
**`input.type`**: `"mongodb"`
|
|
40
|
+
|
|
41
|
+
All operations use `publishProperties.collection` and `messageBody.collection` (both required).
|
|
42
|
+
The connection `name` must match the configured MongoDB connection in Unmeshed.
|
|
43
|
+
|
|
44
|
+
### FIND_ONE
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"type": "mongodb",
|
|
48
|
+
"name": "mongo-test",
|
|
49
|
+
"publishProperties": { "collection": "users" },
|
|
50
|
+
"messageBody": {
|
|
51
|
+
"operation": "FIND_ONE",
|
|
52
|
+
"collection": "users",
|
|
53
|
+
"filter": { "email": "user@example.com" }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
Output: `steps.<ref>.output.results.<field>` (document fields directly on `.results`)
|
|
58
|
+
|
|
59
|
+
### INSERT_ONE
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"messageBody": {
|
|
63
|
+
"operation": "INSERT_ONE",
|
|
64
|
+
"collection": "users",
|
|
65
|
+
"document": {
|
|
66
|
+
"name": "test user",
|
|
67
|
+
"email": "test@example.com",
|
|
68
|
+
"password": "hashed"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### UPDATE_ONE
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"messageBody": {
|
|
78
|
+
"operation": "UPDATE_ONE",
|
|
79
|
+
"collection": "users",
|
|
80
|
+
"filter": { "email": "user@example.com" },
|
|
81
|
+
"update": { "$set": { "name": "New Name" } }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### DELETE_ONE
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"messageBody": {
|
|
90
|
+
"operation": "DELETE_ONE",
|
|
91
|
+
"collection": "users",
|
|
92
|
+
"filter": { "email": "user@example.com" }
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### COUNT
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"messageBody": {
|
|
101
|
+
"operation": "COUNT",
|
|
102
|
+
"collection": "users"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
Output: `steps.<ref>.output.results.count`
|
|
107
|
+
|
|
108
|
+
### CREATE_COLLECTION / DELETE_COLLECTION
|
|
109
|
+
```json
|
|
110
|
+
{ "messageBody": { "operation": "CREATE_COLLECTION", "collection": "my_collection" } }
|
|
111
|
+
{ "messageBody": { "operation": "DELETE_COLLECTION", "collection": "my_collection" } }
|
|
112
|
+
```
|
|
113
|
+
Output: `steps.<ref>.output.results.success` (boolean)
|
|
114
|
+
|
|
115
|
+
### LIST_INDEXES
|
|
116
|
+
```json
|
|
117
|
+
{ "messageBody": { "operation": "LIST_INDEXES", "collection": "users" } }
|
|
118
|
+
```
|
|
119
|
+
Output: `steps.<ref>.output.results.result` (array of index objects, each with a `.key` field)
|
|
120
|
+
|
|
121
|
+
### CREATE_INDEX
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"messageBody": {
|
|
125
|
+
"operation": "CREATE_INDEX",
|
|
126
|
+
"collection": "users",
|
|
127
|
+
"indexName": "my_index",
|
|
128
|
+
"index": { "name": 1, "email": -1 }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
Index direction: `1` = ascending, `-1` = descending
|
|
133
|
+
|
|
134
|
+
### DROP_INDEX
|
|
135
|
+
```json
|
|
136
|
+
{ "messageBody": { "operation": "DROP_INDEX", "collection": "users", "indexName": "my_index" } }
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**MongoDB output access patterns:**
|
|
140
|
+
```javascript
|
|
141
|
+
steps.<ref>.output.results.<field> // FIND_ONE document fields
|
|
142
|
+
steps.<ref>.output.results.count // COUNT
|
|
143
|
+
steps.<ref>.output.results.success // CREATE/DELETE_COLLECTION
|
|
144
|
+
steps.<ref>.output.results.result // LIST_INDEXES (array)
|
|
145
|
+
steps.<ref>.output.results.result.length // number of indexes
|
|
146
|
+
steps.<ref>.output.results.result[n].key // index key definition
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Redis
|
|
152
|
+
|
|
153
|
+
**`input.type`**: `"redis"`
|
|
154
|
+
|
|
155
|
+
`publishProperties` is always `{}`. All config goes in `messageBody`.
|
|
156
|
+
|
|
157
|
+
### SET
|
|
158
|
+
```json
|
|
159
|
+
{
|
|
160
|
+
"type": "redis",
|
|
161
|
+
"name": "redis_integration",
|
|
162
|
+
"publishProperties": {},
|
|
163
|
+
"messageBody": {
|
|
164
|
+
"operation": "SET",
|
|
165
|
+
"key": "user:1",
|
|
166
|
+
"value": "John Doe"
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
`value` can be a string or a JSON object — Redis stores objects as serialized JSON.
|
|
171
|
+
|
|
172
|
+
### GET
|
|
173
|
+
```json
|
|
174
|
+
{ "messageBody": { "operation": "GET", "key": "user:1" } }
|
|
175
|
+
```
|
|
176
|
+
Output: `steps.<ref>.output.results.result` (string) or `steps.<ref>.output.results.<field>` (object fields directly if value was an object)
|
|
177
|
+
|
|
178
|
+
### DEL
|
|
179
|
+
```json
|
|
180
|
+
{ "messageBody": { "operation": "DEL", "keys": ["user:1", "user:2"] } }
|
|
181
|
+
```
|
|
182
|
+
`keys` is always an array.
|
|
183
|
+
|
|
184
|
+
### KEYS (pattern match)
|
|
185
|
+
```json
|
|
186
|
+
{ "messageBody": { "operation": "KEYS", "pattern": "user:*" } }
|
|
187
|
+
```
|
|
188
|
+
Output: `steps.<ref>.output.results.result` (array of matching key strings)
|
|
189
|
+
|
|
190
|
+
### TYPE
|
|
191
|
+
```json
|
|
192
|
+
{ "messageBody": { "operation": "TYPE", "key": "user:1" } }
|
|
193
|
+
```
|
|
194
|
+
Output: `steps.<ref>.output.results.result` (string: `"string"`, `"hash"`, `"list"`, `"set"`, `"zset"`)
|
|
195
|
+
|
|
196
|
+
### RENAME
|
|
197
|
+
```json
|
|
198
|
+
{ "messageBody": { "operation": "RENAME", "oldKey": "user:10", "newKey": "user:profile:10" } }
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### EVAL (Lua scripting)
|
|
202
|
+
```json
|
|
203
|
+
{
|
|
204
|
+
"messageBody": {
|
|
205
|
+
"operation": "EVAL",
|
|
206
|
+
"script": "return redis.call('SET', KEYS[1], ARGV[1])",
|
|
207
|
+
"keys": ["user:2"],
|
|
208
|
+
"args": ["value"]
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
Output: `steps.<ref>.output.results.result`
|
|
213
|
+
|
|
214
|
+
**Redis output access patterns:**
|
|
215
|
+
```javascript
|
|
216
|
+
steps.<ref>.output.results.result // string value, array (KEYS), or Lua script result
|
|
217
|
+
steps.<ref>.output.results.<field> // object fields when value was stored as JSON object
|
|
218
|
+
// Example: if SET key="user:10" value={"name":"Alice","age":30}
|
|
219
|
+
// then GET → steps.<ref>.output.results.name === "Alice"
|
|
220
|
+
// steps.<ref>.output.results.age === 30
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Airtable
|
|
226
|
+
|
|
227
|
+
**`input.type`**: `"airtable"`
|
|
228
|
+
|
|
229
|
+
`publishProperties` is always `{}`. All config goes in `messageBody`.
|
|
230
|
+
|
|
231
|
+
### GET_TABLES
|
|
232
|
+
```json
|
|
233
|
+
{
|
|
234
|
+
"type": "airtable",
|
|
235
|
+
"name": "airtable-test",
|
|
236
|
+
"publishProperties": {},
|
|
237
|
+
"messageBody": {
|
|
238
|
+
"action": "GET_TABLES"
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
Output: `steps.<ref>.output.tables` (array of table objects with `.name`, `.id`)
|
|
243
|
+
Access first table: `steps.<ref>.output.tables[0].name`
|
|
244
|
+
|
|
245
|
+
### LIST (read all records)
|
|
246
|
+
```json
|
|
247
|
+
{
|
|
248
|
+
"messageBody": {
|
|
249
|
+
"action": "LIST",
|
|
250
|
+
"tableName": "{{ steps.get_all_tables.output.tables[0].name }}",
|
|
251
|
+
"pageSize": "100",
|
|
252
|
+
"maxRecords": "100"
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
Output: `steps.<ref>.output.records` (array of record objects)
|
|
257
|
+
Each record: `{ id, fields: { FieldName: value } }`
|
|
258
|
+
|
|
259
|
+
### CREATE
|
|
260
|
+
```json
|
|
261
|
+
{
|
|
262
|
+
"messageBody": {
|
|
263
|
+
"action": "CREATE",
|
|
264
|
+
"tableName": "Table 1",
|
|
265
|
+
"records": [
|
|
266
|
+
{ "fields": { "Name": "Sample", "Email": "user@example.com" } }
|
|
267
|
+
]
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
Output: `steps.<ref>.output.records[0].id` — the created record ID
|
|
272
|
+
|
|
273
|
+
### READ (single record by ID)
|
|
274
|
+
```json
|
|
275
|
+
{
|
|
276
|
+
"messageBody": {
|
|
277
|
+
"action": "READ",
|
|
278
|
+
"tableName": "Table 1",
|
|
279
|
+
"recordId": "{{ steps.<ref>.output.records[0].id }}"
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
Output: `steps.<ref>.output.fields.<FieldName>`
|
|
284
|
+
|
|
285
|
+
### UPDATE
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"messageBody": {
|
|
289
|
+
"action": "UPDATE",
|
|
290
|
+
"tableName": "Table 1",
|
|
291
|
+
"records": [
|
|
292
|
+
{
|
|
293
|
+
"id": "{{ steps.<ref>.output.records[0].id }}",
|
|
294
|
+
"fields": { "Email": "new@example.com" }
|
|
295
|
+
}
|
|
296
|
+
]
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### DELETE
|
|
302
|
+
```json
|
|
303
|
+
{
|
|
304
|
+
"messageBody": {
|
|
305
|
+
"action": "DELETE",
|
|
306
|
+
"tableName": "Table 1",
|
|
307
|
+
"recordIds": ["{{ steps.<ref>.output.records[0].id }}"]
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Airtable rules:**
|
|
313
|
+
- `tableName` can be a static string or a `{{...}}` template (e.g. `steps.get_all_tables.output.tables[0].name`)
|
|
314
|
+
- `pageSize` and `maxRecords` are strings, not numbers: `"100"` not `100`
|
|
315
|
+
- Mark upstream Airtable steps `optional: true` when rate-limiting is possible; use a SWITCH after to check for `HTTP 429` errors
|
|
316
|
+
- Rate-limit check script: `steps.get_all_tables.output.error?.includes("HTTP 429 429 TOO_MANY_REQUESTS")`
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Jira
|
|
321
|
+
|
|
322
|
+
**`input.type`**: `"jira"`
|
|
323
|
+
|
|
324
|
+
All operations use `publishProperties` (no `messageBody` payload). The `action` field in `publishProperties` selects the operation.
|
|
325
|
+
|
|
326
|
+
```json
|
|
327
|
+
{
|
|
328
|
+
"type": "jira",
|
|
329
|
+
"name": "jira-test",
|
|
330
|
+
"publishProperties": {
|
|
331
|
+
"action": "<ACTION>",
|
|
332
|
+
"project_key": "KAN",
|
|
333
|
+
"issue_type": "Task",
|
|
334
|
+
"summary": "Issue summary",
|
|
335
|
+
"description": "Description",
|
|
336
|
+
"priority": "Medium",
|
|
337
|
+
"assignee": "",
|
|
338
|
+
...
|
|
339
|
+
},
|
|
340
|
+
"messageBody": {}
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### GET_ISSUE
|
|
345
|
+
```json
|
|
346
|
+
{ "action": "GET_ISSUE", "issue_key": "KAN-2", "fields": "*all" }
|
|
347
|
+
```
|
|
348
|
+
Output: `steps.<ref>.output.status`, `steps.<ref>.output.summary`, `steps.<ref>.output.description`, etc.
|
|
349
|
+
|
|
350
|
+
### SEARCH_ISSUES
|
|
351
|
+
```json
|
|
352
|
+
{ "action": "SEARCH_ISSUES", "jql": "project = KAN AND issuetype = Bug", "maxResults": "100", "fields": "*all" }
|
|
353
|
+
```
|
|
354
|
+
Output: `steps.<ref>.output.issues` (array)
|
|
355
|
+
Each issue: `steps.<ref>.output.issues[0].key`, `steps.<ref>.output.issues[0].fields.summary`, `steps.<ref>.output.issues[0].fields.description.content[0].content[0].text`
|
|
356
|
+
|
|
357
|
+
### CREATE_ISSUE
|
|
358
|
+
```json
|
|
359
|
+
{
|
|
360
|
+
"action": "CREATE_ISSUE",
|
|
361
|
+
"project_key": "KAN",
|
|
362
|
+
"issue_type": "Task",
|
|
363
|
+
"summary": "Issue summary",
|
|
364
|
+
"description": "Description",
|
|
365
|
+
"priority": "Medium",
|
|
366
|
+
"assignee": "",
|
|
367
|
+
"labels": ["backend", "testing"],
|
|
368
|
+
"components": ["API", "Backend"]
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
Output: `steps.<ref>.output.key` (e.g. `"KAN-5"`)
|
|
372
|
+
|
|
373
|
+
### DELETE_ISSUE
|
|
374
|
+
```json
|
|
375
|
+
{ "action": "DELETE_ISSUE", "issue_key": "{{ steps.create_issue.output.key }}" }
|
|
376
|
+
```
|
|
377
|
+
Output: `steps.<ref>.output.message` (e.g. `"Issue deleted successfully"`)
|
|
378
|
+
|
|
379
|
+
### ADD_COMMENT
|
|
380
|
+
```json
|
|
381
|
+
{ "action": "ADD_COMMENT", "issue_key": "KAN-1", "body": "Comment text" }
|
|
382
|
+
```
|
|
383
|
+
Output: `steps.<ref>.output.id` (comment ID)
|
|
384
|
+
|
|
385
|
+
### GET_COMMENTS
|
|
386
|
+
```json
|
|
387
|
+
{ "action": "GET_COMMENTS", "issue_key": "KAN-1" }
|
|
388
|
+
```
|
|
389
|
+
Output: `steps.<ref>.output.comments` (array)
|
|
390
|
+
Comment text: `steps.<ref>.output.comments[0].body.content[0].content[0].text`
|
|
391
|
+
|
|
392
|
+
### UPDATE_COMMENT
|
|
393
|
+
```json
|
|
394
|
+
{ "action": "UPDATE_COMMENT", "issue_key": "KAN-1", "comment_id": "{{ steps.add_comment.output.id }}", "body": "Updated text" }
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### DELETE_COMMENT
|
|
398
|
+
```json
|
|
399
|
+
{ "action": "DELETE_COMMENT", "issue_key": "KAN-1", "comment_id": "{{ steps.add_comment.output.id }}" }
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### GET_TRANSITIONS
|
|
403
|
+
```json
|
|
404
|
+
{ "action": "GET_TRANSITIONS", "issue_key": "KAN-1" }
|
|
405
|
+
```
|
|
406
|
+
Output: `steps.<ref>.output.transitions` (array of transition objects)
|
|
407
|
+
|
|
408
|
+
### GET_ATTACHMENTS
|
|
409
|
+
```json
|
|
410
|
+
{ "action": "GET_ATTACHMENTS", "issue_key": "KAN-1" }
|
|
411
|
+
```
|
|
412
|
+
Output: `steps.<ref>.output.attachments` (array), each with `.filename`, `.id`, `.content`
|
|
413
|
+
|
|
414
|
+
### SEARCH_USERS
|
|
415
|
+
```json
|
|
416
|
+
{ "action": "SEARCH_USERS", "query": ".", "maxResults": "100" }
|
|
417
|
+
```
|
|
418
|
+
Output: `steps.<ref>.output.users` (array)
|
|
419
|
+
|
|
420
|
+
### GET_CURRENT_USER
|
|
421
|
+
```json
|
|
422
|
+
{ "action": "GET_CURRENT_USER" }
|
|
423
|
+
```
|
|
424
|
+
Output: `steps.<ref>.output.emailAddress`, `steps.<ref>.output.displayName`
|
|
425
|
+
|
|
426
|
+
**Jira rules:**
|
|
427
|
+
- `messageBody` is always `{}`
|
|
428
|
+
- `maxResults` is a string: `"100"` not `100`
|
|
429
|
+
- `fields`: `"*all"` returns all fields; omit to get default fields only
|
|
430
|
+
- Comment body uses Atlassian Document Format (ADF) — nested `content[0].content[0].text` to reach plain text
|
|
431
|
+
- Always add a WAIT step after CREATE_ISSUE before attempting DELETE_ISSUE to avoid race conditions
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## Microsoft Outlook
|
|
436
|
+
|
|
437
|
+
**`input.type`**: `"ms-outlook"`
|
|
438
|
+
|
|
439
|
+
```json
|
|
440
|
+
{
|
|
441
|
+
"type": "ms-outlook",
|
|
442
|
+
"name": "devs-a-outlook-integration",
|
|
443
|
+
"publishProperties": {
|
|
444
|
+
"mailBox": "user@company.io",
|
|
445
|
+
"action": "<ACTION>",
|
|
446
|
+
"folderId": "<folder-id>",
|
|
447
|
+
"folderName": "Inbox",
|
|
448
|
+
"folderParentId": "<parent-folder-id>",
|
|
449
|
+
"receivedFromMinutesAgo": "60",
|
|
450
|
+
"receivedUntilMinutesAgo": "0",
|
|
451
|
+
"readStatusFilter": "UNREAD",
|
|
452
|
+
"messagesInputType": "STEP_REF",
|
|
453
|
+
"markAs": "READ",
|
|
454
|
+
"fromFilter": "",
|
|
455
|
+
"includeAttachments": true
|
|
456
|
+
},
|
|
457
|
+
"messageBody": {}
|
|
458
|
+
}
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
### readEmail
|
|
462
|
+
```json
|
|
463
|
+
{ "action": "readEmail", "mailBox": "user@company.io", "readStatusFilter": "UNREAD", "includeAttachments": true }
|
|
464
|
+
```
|
|
465
|
+
Output: `steps.<ref>.output.messages` (array of email objects)
|
|
466
|
+
Each message: `.subject`, `.from`, `.body`, `.attachments` (array with `.name`, `.contentBytes`)
|
|
467
|
+
Attachments are saved to `/app/files/_outlook_attachments_/` on the Unmeshed filesystem.
|
|
468
|
+
|
|
469
|
+
### sendEmail
|
|
470
|
+
```json
|
|
471
|
+
{
|
|
472
|
+
"action": "sendEmail",
|
|
473
|
+
"mailBox": "DEFAULT",
|
|
474
|
+
"subject": "Email subject",
|
|
475
|
+
"to": "recipient@example.com",
|
|
476
|
+
"body": "<html><p>Email body</p></html>",
|
|
477
|
+
"attachments": "{{ steps.python_1.output.result.attachments }}"
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
- `mailBox: "DEFAULT"` uses the configured mailbox
|
|
481
|
+
- `body` can be HTML string
|
|
482
|
+
- `attachments`: array of objects with `{ name, contentType, type: "CONTENT_BYTES", contentBase64 }` — build this in a preceding PYTHON step
|
|
483
|
+
|
|
484
|
+
### markReadUnread
|
|
485
|
+
```json
|
|
486
|
+
{
|
|
487
|
+
"action": "markReadUnread",
|
|
488
|
+
"markAs": "READ",
|
|
489
|
+
"messagesInputType": "STEP_REF",
|
|
490
|
+
"stepRef": "read"
|
|
491
|
+
}
|
|
492
|
+
```
|
|
493
|
+
- `messagesInputType: "STEP_REF"` — takes messages from a previous readEmail step
|
|
494
|
+
- `stepRef`: the `ref` of the readEmail step to mark
|
|
495
|
+
|
|
496
|
+
**Outlook rules:**
|
|
497
|
+
- `folderId` and `folderParentId` are opaque Outlook folder ID strings (from Outlook API)
|
|
498
|
+
- `receivedFromMinutesAgo` and `receivedUntilMinutesAgo` are strings: `"60"` not `60`
|
|
499
|
+
- `messageBody` is always `{}`
|
|
500
|
+
- Use `errorPolicyName: "retry_ep"` on readEmail steps — email delivery can have slight delays
|
|
501
|
+
- Pattern: SUB_PROCESS (trigger send) → WAIT (allow delivery) → readEmail → assert on messages
|
|
502
|
+
|
|
503
|
+
---
|
|
504
|
+
|
|
505
|
+
## Google Sheets
|
|
506
|
+
|
|
507
|
+
**`input.type`**: `"google-sheets"`
|
|
508
|
+
|
|
509
|
+
### CREATE_SPREADSHEET
|
|
510
|
+
```json
|
|
511
|
+
{
|
|
512
|
+
"type": "google-sheets",
|
|
513
|
+
"name": "g-sheets",
|
|
514
|
+
"publishProperties": {
|
|
515
|
+
"action": "CREATE_SPREADSHEET",
|
|
516
|
+
"title": "{{ steps.<ref>.output.result.<titleField> }}"
|
|
517
|
+
},
|
|
518
|
+
"messageBody": {}
|
|
519
|
+
}
|
|
520
|
+
```
|
|
521
|
+
Output: `steps.<ref>.output.spreadsheetId`
|
|
522
|
+
|
|
523
|
+
### APPEND_ROWS
|
|
524
|
+
```json
|
|
525
|
+
{
|
|
526
|
+
"publishProperties": {
|
|
527
|
+
"action": "APPEND_ROWS",
|
|
528
|
+
"spreadsheetId": "{{ steps.<ref>.output.spreadsheetId }}",
|
|
529
|
+
"range": "Sheet1!A:Z",
|
|
530
|
+
"values": [["col1_value", "col2_value"]]
|
|
531
|
+
},
|
|
532
|
+
"messageBody": {}
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### GET_ROWS
|
|
537
|
+
```json
|
|
538
|
+
{
|
|
539
|
+
"publishProperties": { "action": "GET_ROWS", "spreadsheetId": "<id>", "range": "Sheet1!A:Z" },
|
|
540
|
+
"messageBody": {}
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
Output: `steps.<ref>.output.result.rows`
|
|
544
|
+
|
|
545
|
+
**Rules:**
|
|
546
|
+
- `values` is a 2D array — each inner array is one row
|
|
547
|
+
- Common pattern: CREATE → store ID in PERSISTED_STATE → APPEND on subsequent runs
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## Google Drive
|
|
552
|
+
|
|
553
|
+
**`input.type`**: `"google-drive"`
|
|
554
|
+
|
|
555
|
+
### UPLOAD_FROM_UNMESHED_FILES
|
|
556
|
+
```json
|
|
557
|
+
{
|
|
558
|
+
"type": "google-drive",
|
|
559
|
+
"name": "google-drive",
|
|
560
|
+
"publishProperties": {
|
|
561
|
+
"action": "UPLOAD_FROM_UNMESHED_FILES",
|
|
562
|
+
"folderId": "REPLACE_WITH_GOOGLE_DRIVE_FOLDER_ID",
|
|
563
|
+
"fileName": "{{ steps.<ref>.output.result.fileName }}",
|
|
564
|
+
"fileLocation": "{{ steps.<ref>.output.result.location }}",
|
|
565
|
+
"unmeshedFilePath": "{{ steps.<ref>.output.result.location }}/{{ steps.<ref>.output.result.fileName }}",
|
|
566
|
+
"googleDrivePath": "/"
|
|
567
|
+
},
|
|
568
|
+
"messageBody": {}
|
|
569
|
+
}
|
|
570
|
+
```
|
|
571
|
+
Output: `steps.<ref>.output.id`, `steps.<ref>.output.webViewLink`, `steps.<ref>.output.webContentLink`
|
|
572
|
+
|
|
573
|
+
---
|
|
574
|
+
|
|
575
|
+
## Gmail
|
|
576
|
+
|
|
577
|
+
Gmail has **two separate integration types** depending on whether you only need to send, or also need to read:
|
|
578
|
+
|
|
579
|
+
| `input.type` | `name` | Use when |
|
|
580
|
+
|---|---|---|
|
|
581
|
+
| `"google-gmail-send"` | your send-only connection | sending email only |
|
|
582
|
+
| `"google-gmail-read-send"` | your read+send connection | sending, reading, or marking email |
|
|
583
|
+
|
|
584
|
+
---
|
|
585
|
+
|
|
586
|
+
### Send email (send-only connection)
|
|
587
|
+
|
|
588
|
+
**`input.type`**: `"google-gmail-send"`
|
|
589
|
+
|
|
590
|
+
```json
|
|
591
|
+
{
|
|
592
|
+
"type": "google-gmail-send",
|
|
593
|
+
"name": "unmeshed-mail",
|
|
594
|
+
"publishProperties": {
|
|
595
|
+
"action": "sendEmail",
|
|
596
|
+
"subject": "Your subject here",
|
|
597
|
+
"to": "{{ steps.<ref>.output.result.email }}",
|
|
598
|
+
"cc": "",
|
|
599
|
+
"bcc": "",
|
|
600
|
+
"body": "Email body text or HTML",
|
|
601
|
+
"bodyContentType": "plain",
|
|
602
|
+
"from": "sender@yourdomain.com"
|
|
603
|
+
},
|
|
604
|
+
"messageBody": {}
|
|
605
|
+
}
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
### Send email (read+send connection)
|
|
611
|
+
|
|
612
|
+
**`input.type`**: `"google-gmail-read-send"`
|
|
613
|
+
|
|
614
|
+
```json
|
|
615
|
+
{
|
|
616
|
+
"type": "google-gmail-read-send",
|
|
617
|
+
"name": "gmail-read-send",
|
|
618
|
+
"publishProperties": {
|
|
619
|
+
"action": "sendEmail",
|
|
620
|
+
"subject": "Your subject here",
|
|
621
|
+
"to": "{{ steps.<ref>.output.result.email }}",
|
|
622
|
+
"cc": "",
|
|
623
|
+
"bcc": "",
|
|
624
|
+
"body": "Email body text or HTML",
|
|
625
|
+
"bodyContentType": "plain"
|
|
626
|
+
},
|
|
627
|
+
"messageBody": {}
|
|
628
|
+
}
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
---
|
|
632
|
+
|
|
633
|
+
### Read email
|
|
634
|
+
|
|
635
|
+
**`input.type`**: `"google-gmail-read-send"`
|
|
636
|
+
|
|
637
|
+
```json
|
|
638
|
+
{
|
|
639
|
+
"type": "google-gmail-read-send",
|
|
640
|
+
"name": "unmeshed-mail",
|
|
641
|
+
"publishProperties": {
|
|
642
|
+
"action": "readEmail",
|
|
643
|
+
"userId": "",
|
|
644
|
+
"labelId": "INBOX",
|
|
645
|
+
"receivedFromMinutesAgo": "30",
|
|
646
|
+
"receivedUntilMinutesAgo": "0",
|
|
647
|
+
"fromFilter": "",
|
|
648
|
+
"subjectFilter": "",
|
|
649
|
+
"readStatusFilter": "ALL",
|
|
650
|
+
"includeAttachments": false,
|
|
651
|
+
"maxCount": 10
|
|
652
|
+
},
|
|
653
|
+
"messageBody": {}
|
|
654
|
+
}
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
`readStatusFilter` options: `"ALL"`, `"READ"`, `"UNREAD"`
|
|
658
|
+
|
|
659
|
+
---
|
|
660
|
+
|
|
661
|
+
### Mark read / unread
|
|
662
|
+
|
|
663
|
+
**`input.type`**: `"google-gmail-read-send"`
|
|
664
|
+
|
|
665
|
+
```json
|
|
666
|
+
{
|
|
667
|
+
"type": "google-gmail-read-send",
|
|
668
|
+
"name": "gmail-read-send",
|
|
669
|
+
"publishProperties": {
|
|
670
|
+
"action": "markReadUnread",
|
|
671
|
+
"messagesInputType": "STEP_REF",
|
|
672
|
+
"messageIds": "",
|
|
673
|
+
"stepRef": "<ref-of-readEmail-step>",
|
|
674
|
+
"markAs": "READ"
|
|
675
|
+
},
|
|
676
|
+
"messageBody": {}
|
|
677
|
+
}
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
`messagesInputType`: `"STEP_REF"` to take messages from a prior readEmail step; `stepRef` is that step's `ref`.
|
|
681
|
+
`markAs`: `"READ"` or `"UNREAD"`
|
|
682
|
+
|
|
683
|
+
---
|
|
684
|
+
|
|
685
|
+
**Gmail rules:**
|
|
686
|
+
- `messageBody` is always `{}`
|
|
687
|
+
- `action` is camelCase: `"sendEmail"`, `"readEmail"`, `"markReadUnread"` — not uppercase like other integrations
|
|
688
|
+
- `bodyContentType`: `"plain"` for plain text, `"html"` for HTML body
|
|
689
|
+
- `from` is only on the `google-gmail-send` type — omit it on `google-gmail-read-send`
|
|
690
|
+
- `receivedFromMinutesAgo` and `receivedUntilMinutesAgo` are strings: `"30"` not `30`
|
|
691
|
+
- `maxCount` is a number: `10` not `"10"`
|
|
692
|
+
- The `name` field must match the connection configured in Unmeshed — check with your team which connection name to use
|
|
693
|
+
|
|
694
|
+
---
|
|
695
|
+
|
|
696
|
+
## Slack
|
|
697
|
+
|
|
698
|
+
**`input.type`**: `"slack-messaging"`
|
|
699
|
+
|
|
700
|
+
```json
|
|
701
|
+
{
|
|
702
|
+
"type": "slack-messaging",
|
|
703
|
+
"name": "",
|
|
704
|
+
"publishProperties": { "durationType": "MINUTES", "receiverType": "CHANNEL" },
|
|
705
|
+
"messageBody": {
|
|
706
|
+
"type": "MESSAGE",
|
|
707
|
+
"message": "{{ steps.<ref>.output.result.slackMessage }}",
|
|
708
|
+
"messageBlocks": [
|
|
709
|
+
{ "type": "section", "text": { "type": "mrkdwn", "text": "{{ steps.<ref>.output.result.slackMessage }}" } }
|
|
710
|
+
]
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
**Rules:**
|
|
716
|
+
- `input.type` is `"slack-messaging"` not `"slack"`
|
|
717
|
+
- Build the full message string in a preceding JAVASCRIPT step
|
|
718
|
+
- `receiverType`: `"CHANNEL"` or `"USER"`
|
|
719
|
+
|
|
720
|
+
---
|
|
721
|
+
|
|
722
|
+
## Notion
|
|
723
|
+
|
|
724
|
+
**`input.type`**: `"notion"`
|
|
725
|
+
|
|
726
|
+
All operations go in `messageBody.operation`. `publishProperties` is `{}` or omitted.
|
|
727
|
+
|
|
728
|
+
### CREATE_DATABASE
|
|
729
|
+
```json
|
|
730
|
+
{
|
|
731
|
+
"messageBody": {
|
|
732
|
+
"operation": "CREATE_DATABASE",
|
|
733
|
+
"parent": { "page_id": "<page-id>" },
|
|
734
|
+
"title": [{ "type": "text", "text": { "content": "DB Title" } }],
|
|
735
|
+
"properties": {
|
|
736
|
+
"Name": { "title": {} },
|
|
737
|
+
"Status": { "select": { "options": [{ "name": "Active", "color": "green" }] } },
|
|
738
|
+
"Due Date": { "date": {} },
|
|
739
|
+
"Tags": { "multi_select": { "options": [{ "name": "Bug", "color": "red" }] } },
|
|
740
|
+
"Progress": { "number": { "format": "percent" } },
|
|
741
|
+
"Notes": { "rich_text": {} },
|
|
742
|
+
"Completed": { "checkbox": {} }
|
|
743
|
+
},
|
|
744
|
+
"icon": { "emoji": "🗂️" },
|
|
745
|
+
"cover": { "external": { "url": "https://..." } }
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
```
|
|
749
|
+
Output: `steps.<ref>.output.id` (database ID)
|
|
750
|
+
|
|
751
|
+
### GET_DATABASE / UPDATE_DATABASE
|
|
752
|
+
```json
|
|
753
|
+
{ "operation": "GET_DATABASE", "database_id": "{{ steps.create_database.output.id }}" }
|
|
754
|
+
{ "operation": "UPDATE_DATABASE", "database_id": "...", "title": [...], "description": [...] }
|
|
755
|
+
```
|
|
756
|
+
Output: `steps.<ref>.output.title[0].plain_text`
|
|
757
|
+
|
|
758
|
+
### QUERY_DATABASE
|
|
759
|
+
```json
|
|
760
|
+
{
|
|
761
|
+
"operation": "QUERY_DATABASE",
|
|
762
|
+
"database_id": "{{ steps.create_database.output.id }}",
|
|
763
|
+
"filter": { "property": "Status", "select": { "equals": "In Progress" } },
|
|
764
|
+
"sorts": [{ "property": "Due Date", "direction": "ascending" }],
|
|
765
|
+
"page_size": 50
|
|
766
|
+
}
|
|
767
|
+
```
|
|
768
|
+
Filter types: `select`, `multi_select` (`contains`), `date`, `checkbox`, `rich_text`, `number`
|
|
769
|
+
Output: `steps.<ref>.output.results` (array of page objects)
|
|
770
|
+
Record name: `steps.<ref>.output.results[0].properties.Name.title[0].plain_text`
|
|
771
|
+
|
|
772
|
+
### CREATE_PAGE
|
|
773
|
+
```json
|
|
774
|
+
{
|
|
775
|
+
"operation": "CREATE_PAGE",
|
|
776
|
+
"parent": { "database_id": "{{ steps.create_database.output.id }}" },
|
|
777
|
+
"properties": {
|
|
778
|
+
"Name": { "title": [{ "text": { "content": "Page Title" } }] },
|
|
779
|
+
"Status": { "select": { "name": "In Progress" } },
|
|
780
|
+
"Tags": { "multi_select": [{ "name": "Feature" }] },
|
|
781
|
+
"Due Date": { "date": { "start": "2025-10-31" } },
|
|
782
|
+
"Progress": { "number": 0.45 },
|
|
783
|
+
"Notes": { "rich_text": [{ "text": { "content": "Note text" } }] },
|
|
784
|
+
"Completed": { "checkbox": false }
|
|
785
|
+
},
|
|
786
|
+
"children": [
|
|
787
|
+
{ "object": "block", "type": "heading_2", "heading_2": { "rich_text": [{ "text": { "content": "Section" } }] } },
|
|
788
|
+
{ "object": "block", "type": "paragraph", "paragraph": { "rich_text": [{ "text": { "content": "Text" } }] } },
|
|
789
|
+
{ "object": "block", "type": "to_do", "to_do": { "rich_text": [{ "text": { "content": "Task" } }], "checked": false } }
|
|
790
|
+
],
|
|
791
|
+
"icon": { "emoji": "🔐" }
|
|
792
|
+
}
|
|
793
|
+
```
|
|
794
|
+
Output: `steps.<ref>.output.id` (page ID)
|
|
795
|
+
|
|
796
|
+
### GET_PAGE / UPDATE_PAGE / DELETE_PAGE / ARCHIVE_PAGE
|
|
797
|
+
```json
|
|
798
|
+
{ "operation": "GET_PAGE", "page_id": "{{ steps.create_page_task_1.output.id }}" }
|
|
799
|
+
{ "operation": "UPDATE_PAGE", "page_id": "...", "properties": { ... }, "icon": { "emoji": "🚀" } }
|
|
800
|
+
{ "operation": "UPDATE_PAGE", "page_id": "...", "archived": true } // archive
|
|
801
|
+
{ "operation": "DELETE_PAGE", "page_id": "..." }
|
|
802
|
+
```
|
|
803
|
+
Output: `steps.<ref>.output.properties.Notes.rich_text[0].plain_text`
|
|
804
|
+
Archived check: `steps.<ref>.output.archived` (boolean)
|
|
805
|
+
Trashed check: `steps.<ref>.output.in_trash` (boolean)
|
|
806
|
+
|
|
807
|
+
### GET_PAGE_PROPERTIES
|
|
808
|
+
```json
|
|
809
|
+
{ "operation": "GET_PAGE_PROPERTIES", "page_id": "...", "property_id": "title" }
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
### GET_BLOCK / UPDATE_BLOCK / DELETE_BLOCK
|
|
813
|
+
```json
|
|
814
|
+
{ "operation": "GET_BLOCK", "block_id": "{{ steps.create_page_task_1.output.id }}" }
|
|
815
|
+
{ "operation": "UPDATE_BLOCK", "block_id": "{{ steps.get_block_children.output.results[0].id }}", "heading_2": { "rich_text": [{ "text": { "content": "Updated" } }] } }
|
|
816
|
+
{ "operation": "DELETE_BLOCK", "block_id": "..." }
|
|
817
|
+
```
|
|
818
|
+
Output GET_BLOCK: `steps.<ref>.output.child_page.title` (for page blocks), `steps.<ref>.output.heading_2.rich_text[0].plain_text` (for heading blocks)
|
|
819
|
+
Trashed check: `steps.<ref>.output.in_trash`
|
|
820
|
+
|
|
821
|
+
### GET_BLOCK_CHILDREN / APPEND_BLOCK_CHILDREN
|
|
822
|
+
```json
|
|
823
|
+
{ "operation": "GET_BLOCK_CHILDREN", "block_id": "...", "page_size": 100 }
|
|
824
|
+
{
|
|
825
|
+
"operation": "APPEND_BLOCK_CHILDREN",
|
|
826
|
+
"block_id": "...",
|
|
827
|
+
"children": [
|
|
828
|
+
{ "object": "block", "type": "divider", "divider": {} },
|
|
829
|
+
{ "object": "block", "type": "callout", "callout": { "rich_text": [{ "text": { "content": "Note" } }], "icon": { "emoji": "⚠️" }, "color": "yellow_background" } },
|
|
830
|
+
{ "object": "block", "type": "numbered_list_item", "numbered_list_item": { "rich_text": [{ "text": { "content": "Step 1" } }] } },
|
|
831
|
+
{ "object": "block", "type": "bulleted_list_item", "bulleted_list_item": { "rich_text": [{ "text": { "content": "Item" } }] } },
|
|
832
|
+
{ "object": "block", "type": "quote", "quote": { "rich_text": [{ "text": { "content": "Quote text" } }] } },
|
|
833
|
+
{ "object": "block", "type": "toggle", "toggle": { "rich_text": [{ "text": { "content": "Toggle title" } }], "children": [...] } }
|
|
834
|
+
]
|
|
835
|
+
}
|
|
836
|
+
```
|
|
837
|
+
Output GET_BLOCK_CHILDREN: `steps.<ref>.output.results` (array), each with `.id`, `.type`
|
|
838
|
+
|
|
839
|
+
### SEARCH
|
|
840
|
+
```json
|
|
841
|
+
{
|
|
842
|
+
"operation": "SEARCH",
|
|
843
|
+
"query": "Authentication",
|
|
844
|
+
"filter": { "property": "object", "value": "page" },
|
|
845
|
+
"sort": { "direction": "descending", "timestamp": "last_edited_time" },
|
|
846
|
+
"page_size": 20
|
|
847
|
+
}
|
|
848
|
+
```
|
|
849
|
+
`filter.value`: `"page"` or `"database"` — omit `filter` to search both
|
|
850
|
+
Output: `steps.<ref>.output.results` (array), `steps.<ref>.output.has_more` (boolean)
|
|
851
|
+
|
|
852
|
+
### LIST_USERS / GET_USER
|
|
853
|
+
```json
|
|
854
|
+
{ "operation": "LIST_USERS", "page_size": 100 }
|
|
855
|
+
{ "operation": "GET_USER", "user_id": "{{ steps.list_users.output.results[0].id }}" }
|
|
856
|
+
```
|
|
857
|
+
Output LIST_USERS: `steps.<ref>.output.results` (array), each with `.id`, `.type` (`"person"`), `.name`
|
|
858
|
+
Output GET_USER: `steps.<ref>.output.person.email`
|
|
859
|
+
|
|
860
|
+
**Notion rules:**
|
|
861
|
+
- `publishProperties` is `{}` or omitted — all config in `messageBody`
|
|
862
|
+
- `page_id` and `database_id` are 32-char hex strings (no hyphens in Unmeshed, but Notion API accepts both)
|
|
863
|
+
- Block types supported in children: `heading_2`, `heading_3`, `paragraph`, `to_do`, `divider`, `callout`, `numbered_list_item`, `bulleted_list_item`, `quote`, `toggle`
|
|
864
|
+
- `toggle` blocks support `children` array for nested content
|
|
865
|
+
- Use `errorPolicyName: "retry_ep"` on QUERY_DATABASE steps that run soon after mutations
|
|
866
|
+
- `UPDATE_PAGE` with `"archived": true` archives the page (soft delete); `DELETE_PAGE` puts it in trash (`in_trash: true`)
|
|
867
|
+
|
|
868
|
+
---
|
|
869
|
+
|
|
870
|
+
## PostgreSQL / MySQL
|
|
871
|
+
|
|
872
|
+
**`input.type`**: `"postgres"` or `"mysql"`
|
|
873
|
+
|
|
874
|
+
```json
|
|
875
|
+
{
|
|
876
|
+
"type": "postgres",
|
|
877
|
+
"name": "postgres",
|
|
878
|
+
"publishProperties": {
|
|
879
|
+
"action": "EXECUTE_QUERY",
|
|
880
|
+
"query": "INSERT INTO table_name (col1, col2) VALUES ($1, $2)",
|
|
881
|
+
"params": ["{{ steps.<ref>.output.result.field1 }}", "{{ steps.<ref>.output.result.field2 }}"]
|
|
882
|
+
},
|
|
883
|
+
"messageBody": {}
|
|
884
|
+
}
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
Output: `steps.<ref>.output.result.rows` (SELECT), `steps.<ref>.output.result.rowCount` (INSERT/UPDATE/DELETE)
|
|
888
|
+
|
|
889
|
+
**Rules:**
|
|
890
|
+
- Always parameterise queries — `$1`, `$2` for Postgres; `?` for MySQL
|
|
891
|
+
- `params` is always an array of strings/values
|
|
892
|
+
|
|
893
|
+
---
|
|
894
|
+
|
|
895
|
+
## HTTP / REST API (as INTEGRATION)
|
|
896
|
+
|
|
897
|
+
**`input.type`**: `"http"`
|
|
898
|
+
|
|
899
|
+
```json
|
|
900
|
+
{
|
|
901
|
+
"type": "http",
|
|
902
|
+
"name": "http",
|
|
903
|
+
"publishProperties": {
|
|
904
|
+
"method": "POST",
|
|
905
|
+
"url": "https://api.example.com/endpoint",
|
|
906
|
+
"headers": { "Content-Type": "application/json", "Authorization": "Bearer <token>" }
|
|
907
|
+
},
|
|
908
|
+
"messageBody": { "field1": "{{ steps.<ref>.output.result.value }}" }
|
|
909
|
+
}
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
Output: `steps.<ref>.output.result` (response body), `steps.<ref>.output.status` (HTTP status code)
|
|
913
|
+
|
|
914
|
+
**Note:** There is also a native `HTTP` step type (uppercase) with polling support — see step-types.md.
|