@elastic.io/mcp 1.0.0-beta

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.
Files changed (174) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +173 -0
  3. package/README.npm.md +173 -0
  4. package/dist/api/client.d.ts +12 -0
  5. package/dist/api/client.d.ts.map +1 -0
  6. package/dist/api/client.js +129 -0
  7. package/dist/api/client.js.map +1 -0
  8. package/dist/api/jsonapi.d.ts +21 -0
  9. package/dist/api/jsonapi.d.ts.map +1 -0
  10. package/dist/api/jsonapi.js +32 -0
  11. package/dist/api/jsonapi.js.map +1 -0
  12. package/dist/api/logger.d.ts +8 -0
  13. package/dist/api/logger.d.ts.map +1 -0
  14. package/dist/api/logger.js +43 -0
  15. package/dist/api/logger.js.map +1 -0
  16. package/dist/api/samples.d.ts +15 -0
  17. package/dist/api/samples.d.ts.map +1 -0
  18. package/dist/api/samples.js +465 -0
  19. package/dist/api/samples.js.map +1 -0
  20. package/dist/api/verification.d.ts +6 -0
  21. package/dist/api/verification.d.ts.map +1 -0
  22. package/dist/api/verification.js +71 -0
  23. package/dist/api/verification.js.map +1 -0
  24. package/dist/cli.d.ts +3 -0
  25. package/dist/cli.d.ts.map +1 -0
  26. package/dist/cli.js +9 -0
  27. package/dist/cli.js.map +1 -0
  28. package/dist/config.d.ts +15 -0
  29. package/dist/config.d.ts.map +1 -0
  30. package/dist/config.js +44 -0
  31. package/dist/config.js.map +1 -0
  32. package/dist/embeddings/factory.d.ts +7 -0
  33. package/dist/embeddings/factory.d.ts.map +1 -0
  34. package/dist/embeddings/factory.js +25 -0
  35. package/dist/embeddings/factory.js.map +1 -0
  36. package/dist/embeddings/google.d.ts +19 -0
  37. package/dist/embeddings/google.d.ts.map +1 -0
  38. package/dist/embeddings/google.js +132 -0
  39. package/dist/embeddings/google.js.map +1 -0
  40. package/dist/embeddings/types.d.ts +27 -0
  41. package/dist/embeddings/types.d.ts.map +1 -0
  42. package/dist/embeddings/types.js +2 -0
  43. package/dist/embeddings/types.js.map +1 -0
  44. package/dist/index.d.ts +3 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +227 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/paths.d.ts +2 -0
  49. package/dist/paths.d.ts.map +1 -0
  50. package/dist/paths.js +10 -0
  51. package/dist/paths.js.map +1 -0
  52. package/dist/server.d.ts +29 -0
  53. package/dist/server.d.ts.map +1 -0
  54. package/dist/server.js +385 -0
  55. package/dist/server.js.map +1 -0
  56. package/dist/setup.d.ts +3 -0
  57. package/dist/setup.d.ts.map +1 -0
  58. package/dist/setup.js +338 -0
  59. package/dist/setup.js.map +1 -0
  60. package/dist/tools/confirmation.d.ts +37 -0
  61. package/dist/tools/confirmation.d.ts.map +1 -0
  62. package/dist/tools/confirmation.js +120 -0
  63. package/dist/tools/confirmation.js.map +1 -0
  64. package/dist/tools/constants.d.ts +28 -0
  65. package/dist/tools/constants.d.ts.map +1 -0
  66. package/dist/tools/constants.js +92 -0
  67. package/dist/tools/constants.js.map +1 -0
  68. package/dist/tools/definitions.d.ts +3 -0
  69. package/dist/tools/definitions.d.ts.map +1 -0
  70. package/dist/tools/definitions.js +915 -0
  71. package/dist/tools/definitions.js.map +1 -0
  72. package/dist/tools/flow-build.d.ts +18 -0
  73. package/dist/tools/flow-build.d.ts.map +1 -0
  74. package/dist/tools/flow-build.js +295 -0
  75. package/dist/tools/flow-build.js.map +1 -0
  76. package/dist/tools/flow-evidence.d.ts +62 -0
  77. package/dist/tools/flow-evidence.d.ts.map +1 -0
  78. package/dist/tools/flow-evidence.js +128 -0
  79. package/dist/tools/flow-evidence.js.map +1 -0
  80. package/dist/tools/flow-state-machine.d.ts +69 -0
  81. package/dist/tools/flow-state-machine.d.ts.map +1 -0
  82. package/dist/tools/flow-state-machine.js +292 -0
  83. package/dist/tools/flow-state-machine.js.map +1 -0
  84. package/dist/tools/handlers/auth.d.ts +3 -0
  85. package/dist/tools/handlers/auth.d.ts.map +1 -0
  86. package/dist/tools/handlers/auth.js +410 -0
  87. package/dist/tools/handlers/auth.js.map +1 -0
  88. package/dist/tools/handlers/component.d.ts +3 -0
  89. package/dist/tools/handlers/component.d.ts.map +1 -0
  90. package/dist/tools/handlers/component.js +165 -0
  91. package/dist/tools/handlers/component.js.map +1 -0
  92. package/dist/tools/handlers/data-sample.d.ts +3 -0
  93. package/dist/tools/handlers/data-sample.d.ts.map +1 -0
  94. package/dist/tools/handlers/data-sample.js +83 -0
  95. package/dist/tools/handlers/data-sample.js.map +1 -0
  96. package/dist/tools/handlers/dynamic.d.ts +3 -0
  97. package/dist/tools/handlers/dynamic.d.ts.map +1 -0
  98. package/dist/tools/handlers/dynamic.js +139 -0
  99. package/dist/tools/handlers/dynamic.js.map +1 -0
  100. package/dist/tools/handlers/flow.d.ts +9 -0
  101. package/dist/tools/handlers/flow.d.ts.map +1 -0
  102. package/dist/tools/handlers/flow.js +427 -0
  103. package/dist/tools/handlers/flow.js.map +1 -0
  104. package/dist/tools/handlers/log.d.ts +3 -0
  105. package/dist/tools/handlers/log.d.ts.map +1 -0
  106. package/dist/tools/handlers/log.js +92 -0
  107. package/dist/tools/handlers/log.js.map +1 -0
  108. package/dist/tools/handlers/organization.d.ts +3 -0
  109. package/dist/tools/handlers/organization.d.ts.map +1 -0
  110. package/dist/tools/handlers/organization.js +39 -0
  111. package/dist/tools/handlers/organization.js.map +1 -0
  112. package/dist/tools/handlers/raw.d.ts +3 -0
  113. package/dist/tools/handlers/raw.d.ts.map +1 -0
  114. package/dist/tools/handlers/raw.js +80 -0
  115. package/dist/tools/handlers/raw.js.map +1 -0
  116. package/dist/tools/handlers/search.d.ts +3 -0
  117. package/dist/tools/handlers/search.d.ts.map +1 -0
  118. package/dist/tools/handlers/search.js +105 -0
  119. package/dist/tools/handlers/search.js.map +1 -0
  120. package/dist/tools/handlers/session.d.ts +3 -0
  121. package/dist/tools/handlers/session.d.ts.map +1 -0
  122. package/dist/tools/handlers/session.js +22 -0
  123. package/dist/tools/handlers/session.js.map +1 -0
  124. package/dist/tools/handlers.d.ts +12 -0
  125. package/dist/tools/handlers.d.ts.map +1 -0
  126. package/dist/tools/handlers.js +202 -0
  127. package/dist/tools/handlers.js.map +1 -0
  128. package/dist/tools/helpers.d.ts +44 -0
  129. package/dist/tools/helpers.d.ts.map +1 -0
  130. package/dist/tools/helpers.js +195 -0
  131. package/dist/tools/helpers.js.map +1 -0
  132. package/dist/tools/types.d.ts +39 -0
  133. package/dist/tools/types.d.ts.map +1 -0
  134. package/dist/tools/types.js +2 -0
  135. package/dist/tools/types.js.map +1 -0
  136. package/dist/tools/validation.d.ts +35 -0
  137. package/dist/tools/validation.d.ts.map +1 -0
  138. package/dist/tools/validation.js +800 -0
  139. package/dist/tools/validation.js.map +1 -0
  140. package/dist/types/index.d.ts +47 -0
  141. package/dist/types/index.d.ts.map +1 -0
  142. package/dist/types/index.js +2 -0
  143. package/dist/types/index.js.map +1 -0
  144. package/dist/vectorstore/factory.d.ts +7 -0
  145. package/dist/vectorstore/factory.d.ts.map +1 -0
  146. package/dist/vectorstore/factory.js +25 -0
  147. package/dist/vectorstore/factory.js.map +1 -0
  148. package/dist/vectorstore/qdrant.d.ts +11 -0
  149. package/dist/vectorstore/qdrant.d.ts.map +1 -0
  150. package/dist/vectorstore/qdrant.js +107 -0
  151. package/dist/vectorstore/qdrant.js.map +1 -0
  152. package/dist/vectorstore/types.d.ts +28 -0
  153. package/dist/vectorstore/types.d.ts.map +1 -0
  154. package/dist/vectorstore/types.js +2 -0
  155. package/dist/vectorstore/types.js.map +1 -0
  156. package/package.json +66 -0
  157. package/resources/docs/flow-building-guide.md +581 -0
  158. package/resources/docs/rest-api-pagination.md +58 -0
  159. package/resources/docs/standardized-components-guide.md +66 -0
  160. package/resources/docs/system-instructions.md +207 -0
  161. package/resources/flow-examples/batch-flow-1-collect.json +51 -0
  162. package/resources/flow-examples/batch-flow-2-process.json +70 -0
  163. package/resources/flow-examples/code-component-transform.json +65 -0
  164. package/resources/flow-examples/code-component-trigger.json +52 -0
  165. package/resources/flow-examples/code-component-with-credentials-trigger.json +53 -0
  166. package/resources/flow-examples/code-component-with-credentials.json +67 -0
  167. package/resources/flow-examples/filter-step.json +63 -0
  168. package/resources/flow-examples/polling-sync-trigger.json +50 -0
  169. package/resources/flow-examples/router-branching.json +94 -0
  170. package/resources/flow-examples/splitter-re-assemble.json +72 -0
  171. package/resources/flow-examples/splitter-split.json +68 -0
  172. package/resources/flow-examples/timer-query-action-sync.json +68 -0
  173. package/resources/flow-examples/webhook-async-sync.json +49 -0
  174. package/resources/flow-examples/webhook-request-reply.json +63 -0
@@ -0,0 +1,581 @@
1
+ # elastic.io Flow Building Guide
2
+
3
+ This guide describes how to construct and update integration flows on the elastic.io iPaaS platform.
4
+
5
+ ## Flow Architecture
6
+ An elastic.io flow contains a directed graph of steps (nodes) and connections (edges).
7
+
8
+ ### Step ID Format
9
+ Every `step_id` must follow the format `step_N`, where `N` is a positive integer starting from `1`. Use the exact prefix `step_` followed by the numeric index, with no spaces or additional characters.
10
+
11
+ **Valid examples**: `step_1`, `step_2`, `step_3`, ..., `step_N`
12
+
13
+ **Invalid examples**: `step1`, `Step_1`, `step_0`, `step_01`, `my_step_1`, `step_lk_from`
14
+
15
+ > [!CAUTION]
16
+ > Always use this format when specifying a `step_id` in flow-related API requests. The platform rejects non-conforming step IDs and they cause errors in the UI.
17
+
18
+ ## Flow Scheduling (Cron)
19
+ To run a flow on a periodic schedule (e.g. every 10 minutes, daily, etc.):
20
+ 1. Set the flow type to `"ordinary"` (scheduled).
21
+ 2. Set the `"cron"` property under attributes to a standard 5-field cron expression:
22
+ * **Default**: `"cron": "*/10 * * * *"` (every 10 minutes) — applied automatically if not specified
23
+ * `"cron": "*/15 * * * *"` (every 15 minutes)
24
+ * `"cron": "0 * * * *"` (hourly)
25
+ * `"cron": "0 0 * * *"` (daily at midnight)
26
+ 3. The first step of the flow graph must be a scheduling/trigger component, such as the Simple Trigger.
27
+
28
+ ## Common Core Components
29
+ When building flows, you can look up these components or use their IDs:
30
+ 1. **Simple Trigger (Timer)**
31
+ * **Component ID**: `aaa1111111bbb2222222ccc01`
32
+ * **Command**: `elasticio/simple-trigger-component:timer`
33
+ 2. **Email Component**
34
+ * **Component ID**: `aaa1111111bbb2222222ccc02`
35
+ * **Command**: `elasticio/email-component:send`
36
+ 3. **Splitter**
37
+ * **Component ID**: `aaa1111111bbb2222222ccc03`
38
+ * **Command**: `elasticio/splitter:splitOnJsonataExpression` (preferred) or `elasticio/splitter:split` (deprecated)
39
+ * **Key field**: `expression` — a JSONata expression that MUST use `$getPassthrough().step_id.body` to reference the array to split. Do NOT use bare `body`.
40
+ 4. **Node.js Code Component (Custom Logic)**
41
+ * **Component ID**: Discover this using list_components (commonly `aaa1111111bbb2222222ccc04` or `aaa1111111bbb2222222ccc05`).
42
+ * **Command**: Format using the standard template below, with the action ID `execute`.
43
+
44
+ ## Node (Step) Schema & Object-to-String Normalization
45
+ When retrieving a flow config via `get_flow`, the API returns steps with object metadata. However, when patching or creating a flow via `create_flow` or `update_flow`, you MUST normalize these objects into plain strings:
46
+
47
+ 1. **`component_id`**:
48
+ * *Retrieved (get_flow)*: `{ "componentId": "aaa1111111bbb2222222ccc06", ... }`
49
+ * *Constructed (update/create)*: Plain string, e.g., `"aaa1111111bbb2222222ccc06"`
50
+ 2. **`command`**:
51
+ * *Retrieved (get_flow)*: `{ "actionOrTrigger": "receive", "componentVersion": "..." }`
52
+ * *Constructed (update/create)*: Plain string. For all components (like Salesforce v2, Email, Code, HubSpot, etc.), it MUST be formatted dynamically using the fields returned by the `get_component` tool: `"{commandPrefix}:{action_or_trigger_id}@latestVersionId"` (e.g. `"elasticio/email-component:send@latestVersionId"`). Specifying only the action/trigger name (e.g. `"getUpdatedObjectsPolling"` or `"execute"`) will fail validation and result in a backend 500 error.
53
+
54
+ > [!CAUTION]
55
+ > **Deprecated Actions/Triggers**: Before selecting an action or trigger, you MUST check the component descriptor (returned by `get_component`) for the `"deprecated": true` flag on the action/trigger definition. If an action/trigger is deprecated, you MUST NOT use it — select the non-deprecated alternative instead. Common examples:
56
+ > * **REST API V2**: Use `httpRequestAxios` (Axios Library), NOT `httpRequestAction` (Request Library, deprecated)
57
+ > * **REST API V2 trigger**: Use `httpRequestAxiosTrigger`, NOT `httpRequestTrigger` (deprecated)
58
+ > * **Slack**: Use `makeRawRequest` or `upsertObject`, NOT `postMessage` (deprecated)
59
+ > * **Splitter**: Use `splitOnJsonataExpression`, NOT `split` (deprecated)
60
+ >
61
+ > Using deprecated actions causes a "Deprecated" badge in the UI and may lead to removal in future component versions. When updating existing flows that use deprecated actions, replace them with the non-deprecated equivalent.
62
+
63
+ > [!CAUTION]
64
+ > **Trigger vs Action mismatch**: When recommending a component for a flow step, you MUST verify that the component actually has the right type of operations for the intended role:
65
+ > * For the **first step** (trigger position): the component must have at least one **trigger** defined in its descriptor.
66
+ > * For **subsequent steps** (action positions): the component must have at least one **action** defined in its descriptor.
67
+ >
68
+ > Many components are specialized — e.g., `hubspot-webhook-component` only has triggers (it receives webhook events), while `hubspot-component` has both triggers and actions. Similarly, other `-webhook-` or `-listener-` components typically lack actions.
69
+ >
70
+ > Before presenting component options to the user, call `get_component` for each candidate and filter out components that lack the required operation type. Do NOT list components that cannot serve the intended role — this confuses users and wastes time.
71
+
72
+ > [!IMPORTANT]
73
+ > **Webhook Component Credentials**: Although the Webhook component (`elasticio/webhook:receive`) does not require authentication to receive payloads, you MUST always propose attaching credentials to the Webhook step when building flows. Webhook credentials enable HMAC signature verification, API key validation, basic auth, and other security features. Even if the user says "just a simple webhook", explicitly ask whether they want to secure it and offer to set up or attach credentials. Never silently omit credentials on a Webhook step without asking the user.
74
+
75
+ 3. **`credentials_id`** (for Credential-based components):
76
+ * *Retrieved (get_flow)*: `{ "credentialId": "CRED_ID", "credentialName": "..." }`
77
+ * *Constructed (update/create)*: Plain string, e.g., `"CRED_ID"`
78
+ 4. **`secret_id`** (for Secret-based/OAuth2 components, e.g., Salesforce v2, REST API v2, Google Sheets v2, Zoho):
79
+ * *Retrieved (get_flow)*: `{ "secretId": "SECRET_ID", "secretName": "..." }`
80
+ * *Constructed (update/create)*: Plain string, e.g., `"SECRET_ID"`
81
+ 5. **`fields`**: Key-value pairs containing static component-level configuration parameters (e.g. selecting a direction, input formats, output formats, or a code string for the Code component). These are defined by the component descriptor or select models. Do NOT put input metadata fields here (those must be configured in the incoming connection edge's mapper instead).
82
+
83
+ > [!IMPORTANT]
84
+ > **JSONata Fields in Node `fields` (e.g. Splitter `expression`)**: Some component fields use `viewClass: "JSONataView"` and contain JSONata expressions (e.g., the Splitter's `expression` field). These fields follow the **same passthrough path rules** as edge mappers. You MUST use `$getPassthrough().step_id.body` (or `$getPassthrough().step_id.body.fieldName`) to reference data from prior steps — do NOT use bare references like `body` or field names. For example, to split an array received by a Webhook in step_1:
85
+ > * **INCORRECT**: `"expression": "body"` — resolves to empty/undefined
86
+ > * **CORRECT**: `"expression": "$getPassthrough().step_1.body"` — correctly references the Webhook's body
87
+
88
+ ### Data Samples
89
+ Data samples store example input/output data for flow steps, used for building mappings between steps in the UI.
90
+
91
+ **Where data sample IDs are stored:**
92
+ - In the flow draft, each node can have a `selected_data_samples` array containing data sample IDs:
93
+ ```json
94
+ {
95
+ "id": "step_1",
96
+ "component_id": "55ba18e35d04040500000004",
97
+ "command": "elasticio/webhook:receive@...",
98
+ "selected_data_samples": ["6aa3f14f03c95f00125ec437"]
99
+ }
100
+ ```
101
+ - This field is visible via `get_flow_draft` but NOT via `get_flow` (where `nodes_config` may appear empty).
102
+
103
+ **Updating a data sample:**
104
+ - Use `update_data_sample` with the `dataSampleId` and new `body` to replace the sample's `result.body` content. The UI picks up changes immediately — no need to touch the draft.
105
+
106
+ **Creating and attaching a data sample:**
107
+ - Use `create_data_sample` to create a new sample, then include its ID in the node's `selected_data_samples` array when calling `update_flow_draft`.
108
+
109
+ ### Dynamic Dropdowns (Select Models)
110
+ Some component settings (fields) are dropdowns where the user selects from a list of options (e.g., choosing a Salesforce Object Type or a database). These fields are defined under `fields` with `viewClass: "SelectView"` in the descriptor.
111
+ - **Static Select Models**: If the `model` property of the field in the descriptor is a JSON object (e.g., `{"available": "Available", "pending": "Pending"}`), the keys in that object are the static options you can use in `fields`.
112
+ - **Dynamic Select Models**: If the `model` property is a string (e.g., `"getDatabasesModel"` or `"objectTypes"`), it is a dynamic function. To retrieve the valid dropdown options:
113
+ 1. Call the `init_dynamic_select_model` tool, passing the `workspaceId`, `componentId`, `actionOrTriggerName`, and `methodName` (which is the string value of `model`), along with any credential/secret and preceding dependent field configurations (e.g. if selecting a collection depends on the database `db` field).
114
+ 2. Call the `get_scheduled_execution_result` tool with the returned `executionId` (with default `poll: true`) to wait and fetch the populated options object. Use the keys from this object to populate the setting in `fields`.
115
+
116
+ ### Dynamic Metadata
117
+ Some components define their input/output schemas dynamically based on other step configuration settings (e.g., Salesforce or database operations where fields depend on the selected object type). These triggers or actions are defined with `"dynamicMetadata": true` in the component descriptor.
118
+ - To retrieve the dynamic input and output schemas:
119
+ 1. Call the `init_dynamic_metadata` tool, passing the `workspaceId`, `componentId`, and `actionOrTriggerName` (which maps to the module), along with any credential/secret and preceding dependent fields configuration (e.g., `{"sobject": "Account"}`).
120
+ 2. Call the `get_scheduled_execution_result` tool with the returned `executionId` (with default `poll: true`) to wait and retrieve the completed schema.
121
+ 3. The returned schemas are automatically pruned of large enum lists (e.g., country/state codes) to save context tokens. Use these schemas to build JSONata mappings on the step's incoming edge.
122
+
123
+ ### Credential Verification
124
+ Before verifying credentials for a component step:
125
+ - Call the `init_verify_credentials` tool, passing the `workspaceId`, `componentId`, and optional `versionId`.
126
+ - To verify a credential or secret connection, pass its `credentialId` or `secretId`. The tool will automatically retrieve its configured fields and initiate verification. You can optionally pass overrides in the `fields` parameter.
127
+ - The tool polls internally and returns the final verification result directly.
128
+
129
+ There are two primary styles of credential/secret verification based on component authorization types:
130
+ 1. **OAuthFieldView (e.g. Salesforce v2)**: The credentials/tokens must be nested inside an `oauth` object (e.g. containing `access_token`, `refresh_token`, `expires_in`, and a nested `undefined_params` object with `instance_url`, etc.). The tool handles this structure automatically when verifying secrets of type `oauth2`.
131
+ 2. **HTTPAuthView (e.g. REST API v2, Code Component with credentials)**: The fields are flat at the root of the `fields` object. These secrets have a top-level type of `auth-secret`. The sub-types are:
132
+ - **No Auth (`noauth`)**: Represents an empty `credentials` object.
133
+ - **Basic Auth (`basic`)**: Contains flat `username` and `password` fields.
134
+ - **API Key (`api_key`)**: Contains flat `headerName` and `headerValue` fields.
135
+ - **OAuth2 (`oauth2`)**: Contains flat `access_token`, `refresh_token`, `expires_in`, and optionally `scope` and `additional_params` fields.
136
+ 3. **Plain Credentials**: Passed flat at the root of the `fields` object (mapping to `keys` on the credential object, e.g. `apiKey`).
137
+
138
+
139
+ ### Example: Node.js Code Component Step
140
+ To execute custom JavaScript code:
141
+ ```json
142
+ {
143
+ "id": "step_3",
144
+ "component_id": "aaa1111111bbb2222222ccc05",
145
+ "command": "elasticio/code:execute@latestVersionId",
146
+ "name": "Custom JavaScript Code Block",
147
+ "fields": {
148
+ "code": "async function run(msg, cfg) {\n console.log('Input message:', msg);\n const data = msg.body;\n if (!data) return;\n await this.emit('data', { body: { success: true, processed: data } });\n}"
149
+ }
150
+ }
151
+ ```
152
+
153
+ ### Example: Splitter Component Step (Split on JSONata Expression)
154
+ The Splitter's `expression` field is a JSONata expression that MUST use `$getPassthrough()` to reference data from prior steps. Assuming the expression evaluates to an array, each element is emitted as a separate message.
155
+
156
+ #### Splitting a Webhook body (step_1 is Webhook receiving an array)
157
+ ```json
158
+ {
159
+ "id": "step_2",
160
+ "component_id": "aaa1111111bbb2222222ccc03",
161
+ "command": "elasticio/splitter:splitOnJsonataExpression@latestVersionId",
162
+ "name": "Split Items",
163
+ "fields": {
164
+ "expression": "$getPassthrough().step_1.body"
165
+ }
166
+ }
167
+ ```
168
+
169
+ #### Splitting a nested array from REST API response (step_1 is REST API V2 trigger)
170
+ The REST API V2 trigger emits `{ statusCode, HTTPHeaders, responseBody }`. To split the `responseBody` array:
171
+ ```json
172
+ {
173
+ "id": "step_2",
174
+ "component_id": "aaa1111111bbb2222222ccc03",
175
+ "command": "elasticio/splitter:splitOnJsonataExpression@latestVersionId",
176
+ "name": "Split API Response",
177
+ "fields": {
178
+ "expression": "$getPassthrough().step_1.body.responseBody"
179
+ }
180
+ }
181
+ ```
182
+
183
+ #### Splitting a deeply nested array (e.g. `responseBody.data.items`)
184
+ ```json
185
+ {
186
+ "id": "step_2",
187
+ "component_id": "aaa1111111bbb2222222ccc03",
188
+ "command": "elasticio/splitter:splitOnJsonataExpression@latestVersionId",
189
+ "name": "Split Nested Items",
190
+ "fields": {
191
+ "expression": "$getPassthrough().step_1.body.responseBody.data.items"
192
+ }
193
+ }
194
+ ```
195
+
196
+ #### Splitting after a Code component (step_2 is Code that emits an array in `body.results`)
197
+ ```json
198
+ {
199
+ "id": "step_3",
200
+ "component_id": "aaa1111111bbb2222222ccc03",
201
+ "command": "elasticio/splitter:splitOnJsonataExpression@latestVersionId",
202
+ "name": "Split Code Results",
203
+ "fields": {
204
+ "expression": "$getPassthrough().step_2.body.results"
205
+ }
206
+ }
207
+ ```
208
+
209
+ > [!WARNING]
210
+ > **Common Splitter mistakes**:
211
+ > * `"expression": "body"` — bare reference, resolves to empty/undefined
212
+ > * `"expression": "responseBody"` — bare reference, same problem
213
+ > * `"expression": "$getPassthrough().step_1.responseBody"` — missing `.body.` segment; passthrough always wraps in `body`
214
+ > * **CORRECT**: `"expression": "$getPassthrough().step_1.body.responseBody"`
215
+
216
+ > [!CAUTION]
217
+ > **UI validation limitation for deep JSONata paths**: The UI form validator for the Splitter's `expression` field only accepts paths up to `$getPassthrough().step_X.body` (one level after `body`). Deeper paths like `$getPassthrough().step_1.body.data.items` work correctly at runtime but cause a **red configuration indicator** and **blocked Continue button** in the UI.
218
+ >
219
+ > **Workaround**: Instead of splitting a deeply nested array directly, restructure the flow:
220
+ > 1. Have the sender send the array directly as the body (e.g., `[item1, item2, ...]` instead of `{ "data": { "items": [...] } }`), OR
221
+ > 2. Add a **Code component** before the Splitter to extract the nested array and emit it as the body, then use `$getPassthrough().step_X.body` in the Splitter.
222
+ >
223
+ > **Do NOT use deep paths** like `$getPassthrough().step_1.body.responseBody.data.items` even though they are valid JSONata — they will cause UI issues for users editing the flow.
224
+
225
+ ### REST API V2 `reader` Field Format (RESTAPIView)
226
+ Components using `viewClass: "RESTAPIView"` (e.g., REST API V2) store HTTP request configuration in a `reader` field object.
227
+
228
+ #### Headers Format
229
+ The `headers` property inside `reader` **MUST** be an array of `{key, value}` objects with `frozen: true`, NOT a plain object. Header values that are string literals **MUST** use escaped double quotes (JSONata string syntax):
230
+
231
+ **INCORRECT** (causes `TypeError: r.map is not a function` in the frontend):
232
+ ```json
233
+ "headers": {"Content-Type": "application/json"}
234
+ ```
235
+
236
+ **CORRECT:**
237
+ ```json
238
+ "headers": [
239
+ {"key": "Content-Type", "value": "\"application/json\"", "frozen": true}
240
+ ]
241
+ ```
242
+
243
+ #### Body Format for POST/PUT/PATCH
244
+ For HTTP methods that send a request body (POST, PUT, PATCH), the `body` property inside `reader` **MUST** be an object with the following structure — NOT a plain string:
245
+
246
+ ```json
247
+ "body": {
248
+ "raw": "<JSONata expression or static JSON string>",
249
+ "formData": [],
250
+ "urlencoded": [],
251
+ "contentType": "application/json"
252
+ }
253
+ ```
254
+
255
+ * `raw`: The request body content. Can be a JSONata expression (e.g., `$getPassthrough().step_1.body`) or a static JSON string (e.g., `{"city":"London"}`).
256
+ * `formData`: Must be `[]` (empty array) unless using multipart/form-data.
257
+ * `urlencoded`: Must be `[]` (empty array) unless using application/x-www-form-urlencoded.
258
+ * `contentType`: The body content type (e.g., `"application/json"`).
259
+
260
+ > [!WARNING]
261
+ > Omitting `body` for POST/PUT/PATCH methods causes a red validation indicator in the UI. Passing `body` as a plain string instead of an object causes `TypeError: Cannot create property 'raw' on string` in the frontend.
262
+
263
+ #### Complete POST Example
264
+ ```json
265
+ {
266
+ "reader": {
267
+ "url": "\"https://httpbin.org/post\"",
268
+ "method": "POST",
269
+ "headers": [
270
+ {"key": "Content-Type", "value": "\"application/json\"", "frozen": true}
271
+ ],
272
+ "body": {
273
+ "raw": "{\n \"data\": $getPassthrough().step_1.body\n}",
274
+ "formData": [],
275
+ "urlencoded": [],
276
+ "contentType": "application/json"
277
+ }
278
+ }
279
+ }
280
+ ```
281
+
282
+ ## Separation of Concerns: Node Fields vs. Edge Mapper
283
+ An integration step has two separate kinds of configurations that must not be mixed:
284
+
285
+ 1. **Static Configuration (Node Fields)**:
286
+ * Configured under the step's `fields` object.
287
+ * Defines how the component behaves globally (e.g. choosing `"jsonToHjson"` vs `"hjsonToJson"` or selecting an attachment format).
288
+ * Do NOT include input schema fields (fields that receive data from other steps) in `node.fields`.
289
+
290
+ 2. **Data Mapping (Edge Mapper)**:
291
+ * Configured under the incoming edge's `config.mapper` object.
292
+ * Maps outputs of previous steps to the current step's input schema fields (as defined by the component's input metadata schema).
293
+ * **CRITICAL: Respect field nesting from input metadata.** If the component's input metadata defines fields nested inside an object (e.g., Marketo's `values` object containing `email`, `firstName`, etc.), the mapper MUST mirror that nesting structure. Always call `init_dynamic_metadata` first to discover the exact field hierarchy, then build the mapper accordingly. Do NOT flatten nested fields to the top level. If `init_dynamic_metadata` fails (e.g., returns 500 due to broken credentials), do NOT guess the mapping structure — inform the user that the metadata is unavailable and that valid credentials are required to determine the correct field structure.
294
+ * **CRITICAL: Use property keys, not display names.** The UI shows human-readable titles (e.g., "E-mail Address", "First Name") from the JSON Schema `title` property, but the mapper MUST use the actual property keys from the metadata schema (e.g., `email`, `firstName`). These often differ — they may be in camelCase, snake_case, or use completely different words. Always get the correct keys from `init_dynamic_metadata` output.
295
+ * **CRITICAL: Mapper values MUST use real output field names from source steps — NEVER guess or hallucinate field names.** See the "Correct Mapping Algorithm" section below for the required procedure.
296
+ * For example, if the component's input metadata defines a field `jsonToConvert`, the mapper on the incoming edge must contain: `"mapper": { "jsonToConvert": "body" }`. This mapping is NEVER placed inside the step's `fields` object.
297
+
298
+ ### Correct Mapping Algorithm (MANDATORY)
299
+ A mapper has two parts: **keys** (what the target step expects as input) and **values** (where the data comes from in previous steps). Both MUST be derived from real metadata — NEVER guessed or hallucinated.
300
+
301
+ > [!CAUTION]
302
+ > **Do NOT invent or guess field names for mapper values.** The most common and critical mistake is assuming what fields a previous step outputs based on general knowledge of the system (e.g., assuming Salesforce returns `Name`, `Id`, `Phone`). The actual output fields depend on the specific component version, action configuration, and object type — they may differ from what you expect. ALWAYS retrieve the real output schema.
303
+
304
+ **Step-by-step procedure for building a mapper on an edge from `step_X` (source) to `step_Y` (target):**
305
+
306
+ 1. **Retrieve TARGET input schema (mapper keys):**
307
+ * Call `init_dynamic_metadata` for the TARGET step (`step_Y`) with its `componentId`, `actionOrTriggerName`, credentials, and configured `fields`.
308
+ * Poll `get_scheduled_execution_result` to get the result.
309
+ * The `in` (input) schema properties define the mapper **keys** — the field names the target step expects to receive.
310
+
311
+ 2. **Retrieve SOURCE output schema (mapper values):**
312
+ * Call `init_dynamic_metadata` for the SOURCE step (`step_X`) with its `componentId`, `actionOrTriggerName`, credentials, and configured `fields`.
313
+ * Poll `get_scheduled_execution_result` to get the result.
314
+ * The `out` (output) schema properties define what field names the source step **actually emits**. Use ONLY these field names in `$getPassthrough().step_X.body.<field>` paths.
315
+ * If the source step has `dynamicMetadata: true`, you MUST call `init_dynamic_metadata` for it — do NOT skip this step.
316
+ * If the source step does NOT have `dynamicMetadata: true`, check the action/trigger's static `metadata.out` schema from `get_component` instead.
317
+
318
+ 3. **Build the mapper object:**
319
+ * For each field in the target's input schema (`in.properties`), create a mapper entry:
320
+ * **Key**: the property name from the target's input schema
321
+ * **Value**: a JSONata expression using `$getPassthrough().step_X.body.<output_field>` where `<output_field>` is a real field name from the source's output schema
322
+ * If a required target input field has no corresponding source output field, inform the user and ask how to handle it (e.g., use a static literal value, a Code component to transform data, or skip the field).
323
+
324
+ **Example:**
325
+ ```
326
+ Source step (step_1): Salesforce "getUpdatedObjectsPolling" with sobject="Account"
327
+ → init_dynamic_metadata returns out.properties: { "Id": {...}, "Name": {...}, "BillingCity": {...} }
328
+
329
+ Target step (step_2): Email "send"
330
+ → init_dynamic_metadata returns in.properties: { "to": {...}, "subject": {...}, "textBody": {...} }
331
+
332
+ Mapper on edge step_1 → step_2:
333
+ {
334
+ "to": "\"admin@example.com\"",
335
+ "subject": "\"Account Updated: \" & $getPassthrough().step_1.body.Name",
336
+ "textBody": "\"ID: \" & $getPassthrough().step_1.body.Id & \"\\nCity: \" & $getPassthrough().step_1.body.BillingCity"
337
+ }
338
+ ```
339
+
340
+ > [!WARNING]
341
+ > **When updating an existing flow's mapping**, always re-retrieve the output metadata of the source step. Do NOT reuse field names from the existing mapper without verification — the source step's configuration may have changed (e.g., different object type selected), which changes the output schema.
342
+
343
+ ## Edge (Connection) Schema & JSONata Mapping
344
+ Each edge in the `graph.edges` array connects two steps:
345
+ * `id`: Unique identifier (typically `source_id:target_id` or `mapper:source_id:target_id`).
346
+ * `source`: The source step ID.
347
+ * `target`: The target step ID.
348
+ * `config`: Optional mapping config:
349
+ * `mapper_type`: Must be `"jsonata"`
350
+ * `mapper`: A JSONata expression string.
351
+ * > [!IMPORTANT]
352
+ * > **CRITICAL MAPPING RULE (PASSTHROUGH PATHS)**: You MUST ALWAYS use the explicit passthrough path format: `$getPassthrough().step_id.body.field` (e.g., `$getPassthrough().step_1.body.Name`) to reference any properties/fields from prior steps, where `step_id` is the ID of the node that originally produced or fetched the field (e.g., `step_1` for Salesforce). This applies even if it's the immediate predecessor. NEVER reference fields by their plain name (like `Name` or `Id`) or implicit context. They will fail to resolve.
353
+ * > **JSONATA QUOTING AND ESCAPING RULES**:
354
+ * > 1. **Use escaped double quotes (`\"`) for literal strings in JSONata, but do NOT wrap the entire expression or concatenation in outer quotes.**
355
+ * > * **INCORRECT**: `"subject": "\"'Salesforce Account Imported: ' & Name\""`
356
+ * > * **INCORRECT**: `"to": "\"'user@example.com'\""`
357
+ * > * **CORRECT**: `"to": \"user@example.com\"` (escaped double quotes for the string literal)
358
+ * > * **CORRECT**: `"subject": \"Salesforce Account Imported: \" & $getPassthrough().step_1.body.Name` (concatenation, escaped double quotes only around the literal text, not the whole expression!)
359
+ * > * **CORRECT**: `"textBody": \"ID: \" & $getPassthrough().step_1.body.Id & \"\\nName: \" & $getPassthrough().step_1.body.Name`
360
+ * > 2. **Double Escaping Newlines**: To include a newline in a JSONata string literal within the JSON payload, write it as `\\n` (double backslash).
361
+ * > 3. **No Quotes on Path/Variable Expressions**: Never put quotes around path expressions like `$getPassthrough().step_1.body.Name`.
362
+ * > 4. **Preceding Step Output Schema**: Standard message payload emitted is `{ "body": { ... } }`. Therefore, always start with `$getPassthrough().step_id.body.field`.
363
+ * > 5. **Array-typed mapper fields MUST be JSONata strings, NOT JSON arrays.** When a component's input metadata defines a field with `"type": "array"` (e.g., the ChatGPT component's `messages` field), the mapper value MUST be a **JSONata expression string** that evaluates to an array — NOT a native JSON array. The UI cannot render native JSON arrays in the mapper and will show empty fields.
364
+ * > * **INCORRECT** (UI shows empty fields):
365
+ * > ```json
366
+ * > "messages": [{"role": "\"system\"", "content": [{"type": "\"text\"", "text": "\"Hello\""}]}]
367
+ * > ```
368
+ * > * **CORRECT** (UI renders properly):
369
+ * > ```json
370
+ * > "messages": "[{\"role\": \"system\", \"content\": [{\"type\": \"text\", \"text\": \"Hello\"}]}]"
371
+ * > ```
372
+ * > Note: Inside the JSONata string, literal values like `"system"` use standard JSONata double quotes (escaped as `\"` in JSON). Dynamic values use passthrough paths without quotes: `$getPassthrough().step_1.body.field`.
373
+
374
+ > [!CAUTION]
375
+ > **UI visual mapper limitation with array-typed input fields**: When a component's input schema defines a field with `"type": "array"` (e.g., `file_urls`, `messages`, `recipients`), the UI visual mapper **cannot map properties from prior steps into array items**. The mapper dropdown shows "Please note that it is impossible to map properties that are not inside the array property" and no properties are selectable — even if the previous step (e.g. Webhook) has valid output data.
376
+ >
377
+ > **This is a UI-only limitation** — the JSONata mapping set via API works correctly at runtime. However, users editing the flow in the UI will see red validation indicators and empty fields.
378
+ >
379
+ > **When building flows via API with array-typed input fields, you MUST**:
380
+ > 1. Always set the mapper on the edge — never leave the edge without a `config.mapper` for steps that have array-typed inputs.
381
+ > 2. Use a JSONata array expression string (starting with `[`) that constructs the array inline, e.g.:
382
+ > ```
383
+ > "[{\"file_url\": $getPassthrough().step_1.body.file_url, \"filename\": $getPassthrough().step_1.body.filename}]"
384
+ > ```
385
+ > 3. Do NOT use a plain passthrough like `$getPassthrough().step_1.body.file_urls` for array fields — even if the webhook sends an array, the UI cannot render this mapping and shows red indicators.
386
+ >
387
+ > **Inform the user** that the flow will work correctly at runtime, but the UI may show red indicators on array fields. They can verify the mapping by clicking the `<>` (JSONata) toggle in the UI.
388
+
389
+ ### Example: Edge with Array-typed Mapper Field (e.g. ChatGPT messages)
390
+ ```json
391
+ {
392
+ "id": "step_1:step_2",
393
+ "source": "step_1",
394
+ "target": "step_2",
395
+ "config": {
396
+ "mapper_type": "jsonata",
397
+ "mapper": {
398
+ "messages": "[{\"role\": \"system\", \"content\": [{\"type\": \"text\", \"text\": \"Classify this support ticket.\"}]}, {\"role\": \"user\", \"content\": [{\"type\": \"text\", \"text\": $getPassthrough().step_1.body.description}]}]"
399
+ }
400
+ }
401
+ }
402
+ ```
403
+
404
+ ### Example: Edge with Correct JSONata Mapper
405
+ ```json
406
+ {
407
+ "id": "step_2:step_3",
408
+ "source": "step_2",
409
+ "target": "step_3",
410
+ "config": {
411
+ "mapper_type": "jsonata",
412
+ "mapper": {
413
+ "to": "\"user@example.com\"",
414
+ "subject": "\"Salesforce Account Imported: \" & $getPassthrough().step_1.body.Name",
415
+ "textBody": "\"Salesforce Account Details:\\nID: \" & $getPassthrough().step_1.body.Id & \"\\nName: \" & $getPassthrough().step_1.body.Name & \"\\nPhone: \" & $getPassthrough().step_1.body.Phone & \"\\nWebsite: \" & $getPassthrough().step_1.body.Website"
416
+ }
417
+ }
418
+ }
419
+ ```
420
+
421
+ > [!NOTE]
422
+ > **Supported Mapper Formats**:
423
+ > Elastic.io supports two mapper formats for `edge.config.mapper`:
424
+ > 1. **Per-field Object Mapper** (`{"fieldName": "expression"}`): Standard format for simple/flat component mappings where each property is mapped individually in the visual UI. Nested objects and arrays of objects/expressions are fully supported.
425
+ > 2. **Root JSONata String Mapper** (`"{\n \"field\": expression\n}"` or `"(\n $var := ...;\n {\n \"field\": ...\n }\n)"`): Ideal for complex payloads, nested objects/arrays, or developer-mode mappings requiring JSONata block variables. Ensure the expression starts with `{`, `(`, or `[`, and is NOT wrapped in outer literal quotes.
426
+
427
+ ## Read-Only Properties to Strip
428
+ When sending an `update_flow` (PATCH) payload, the API will fail if read-only properties are present in the request. You MUST strip the following keys from `attributes` and `relationships`:
429
+
430
+ | Section | Properties to Delete/Exclude |
431
+ |---|---|
432
+ | **attributes** | `created_at`, `updated_at`, `status`, `current_status`, `active`, `status_changed_at`, `type`, `container_state`, `error_count`, `api_version`, `links`, `stats_enabled` |
433
+ | **relationships** | `user`, `contract`, `versions`, `latest_version`, `workspace` (exclude workspace on update, keep only on create) |
434
+
435
+ ## Safe Flow Modification Lifecycle
436
+ To change the graph of the flow or replace one component with another:
437
+ 1. **Stop if Active**: If the flow is active, stop it using `stop_flow` (POST `/flows/:id/stop`).
438
+ 2. **Wait for Shutdown**: Query the flow configuration using `get_flow` and check that it is fully inactive:
439
+ `data.attributes.status === data.attributes.current_status === "inactive"`
440
+ (Wait/poll since container cleanup can take 10-30 seconds).
441
+ 3. **Check for Existing Draft**: Call `get_flow_draft` (GET `/flows/:id/draft`) to retrieve the current draft. If 404, no draft exists — the first `update_flow_draft` call will create one.
442
+ 4. **Apply Changes to Draft**: Use `update_flow_draft` (PUT `/flows/:id/draft`) to make modifications on the draft. For multiple changes, call `update_flow_draft` iteratively — one change at a time or in logical batches. Do NOT modify the flow directly via `update_flow` until the draft is finalized.
443
+ 5. **Publish Draft**: Once the draft is confirmed, call `update_flow` (PATCH `/flows/:id`) to publish the draft and promote it to the active flow configuration.
444
+ 6. **Start Flow**: Restart the flow by calling `start_flow` (POST `/flows/:id/start`).
445
+
446
+ ## Recommended Flow Creation Strategy (Empty → Skeleton → Mappers → Publish)
447
+ When building flows, use a three-stage draft approach. The key principle: **`data` (published config) stays empty until the final Publish step** — all construction happens on the **draft**, just like in the UI.
448
+
449
+ ### Stage 1: Create Empty Flow
450
+ Use `create_flow` with **minimal attributes only** — no nodes, no edges, no graph:
451
+ - Include only: `name`, `type` (e.g. `"ordinary"`), `cron` (if scheduled), and `workspace_id`.
452
+ - Do NOT include `graph`, `nodes`, `edges`, or step configurations.
453
+ - This creates an empty flow with an empty `data` section and a blank draft — exactly like clicking "Add New Flow" in the UI.
454
+
455
+ ### Stage 2: Build Skeleton on Draft
456
+ Use `update_flow_draft` to add the flow structure (nodes and edges without mappers):
457
+ 1. Call `get_flow(flowId)` to confirm the flow exists.
458
+ 2. Call `get_flow_draft(flowId)` to retrieve the current (empty) draft.
459
+ 3. Call `update_flow_draft(flowId, body)` with all nodes (`component_id`, `command`, `credentials_id`/`secret_id`, `name`, static `fields`) and edges (`source`, `target` only — no `config.mapper`).
460
+ 4. This validates the structure (components, credentials, graph topology) independently of JSONata mappings.
461
+
462
+ ### Stage 3: Add Mappers on Draft
463
+ Use `update_flow_draft` to iteratively add mappers to edges:
464
+ 1. Call `get_flow_draft(flowId)` to retrieve the current draft (now containing the skeleton).
465
+ 2. For each edge that needs a mapper:
466
+ - Retrieve the **target step's input schema** — call `init_dynamic_metadata` (if `dynamicMetadata: true`) or use static `metadata.in` from `get_component`.
467
+ - Retrieve the **source step's output schema** — call `init_dynamic_metadata` or use static `metadata.out` from `get_component`.
468
+ - Build the mapper object using real field names via `$getPassthrough().step_id.body.field` paths.
469
+ 3. Call `update_flow_draft(flowId, body)` with the updated graph containing `config.mapper` and `config.mapper_type: "jsonata"` on the relevant edges.
470
+ 4. Repeat per edge or in batches — each `update_flow_draft` call updates the draft in place.
471
+
472
+ > [!TIP]
473
+ > The draft is visible in the UI throughout all stages. The user can open the flow and see it being built step by step — just like editing in the UI.
474
+
475
+ > [!IMPORTANT]
476
+ > **Data Sample Updates**: After changing a step's configuration (`fields`) or an edge's mapper, you MUST ask the user whether they want to update/regenerate the data sample for the affected step(s). Stale data samples cause incorrect field previews in the UI, making the flow confusing for users who later edit it manually. Use `debug_flow_step` or `create_data_sample` to regenerate samples.
477
+
478
+ ### Publish
479
+ Once all mappers are configured and verified on the draft:
480
+ - Use `update_flow(flowId, body)` (PATCH) to publish the draft — this promotes the draft to `data` (the active flow configuration).
481
+ - Before this step, `data` remains empty. After publishing, `data` contains the full flow configuration from the draft.
482
+ - Optionally call `start_flow` to activate.
483
+
484
+ ### Benefits
485
+ - **Matches UI behavior**: `data` stays empty until publish, just like manual flow creation in the UI.
486
+ - **Separation of concerns**: Stage 2 validates structure; Stage 3 validates mappings. If `update_flow_draft` fails in Stage 2 → structural issue. If it fails in Stage 3 → mapper issue.
487
+ - **Iterative**: The skeleton and mappers can be built incrementally, verified against real schemas from `init_dynamic_metadata`.
488
+ - **UI-friendly**: The flow appears in the UI immediately after Stage 1; the user can inspect progress in real time as the draft evolves.
489
+ - **Metadata-driven**: Dynamic metadata calls work better when the flow already exists and step dependencies are resolvable.
490
+ - **Safe**: Nothing is published until explicitly confirmed — all work stays on the draft.
491
+
492
+ ## UI Configuration vs. REST API Payload Separation
493
+ When helping users or explaining component configurations, always distinguish between **what the user configures on the UI** and **what the LLM sends to the elastic.io REST API**. These are entirely different concepts:
494
+
495
+ > [!WARNING]
496
+ > DO NOT confuse UI Settings/Fields and Input Mappings with REST API Nodes and Edges when explaining component configuration to a user. Always present UI-oriented instructions (e.g., "Set the Object Type setting to 'Account' and map the 'First Name' input field to...") unless the user is asking how to build/update a flow via the REST API or using tools like create_flow / update_flow.
497
+
498
+ ### 1. What the User Configures in the Browser UI
499
+ When a user sets up a component (e.g. Salesforce or Webhook) in the browser, they configure:
500
+ * **Fields / Settings**: The static configuration inputs (e.g., selecting "Account" as the object type or setting "Direction" to "jsonToHjson"). These are metadata/descriptor-driven.
501
+ * **Mappings (Input Schema Fields)**: Mappings connecting previous steps' output fields to the current step's input fields (e.g., mapping an incoming "company_name" from a Webhook to the Salesforce "Name" field). These input fields are defined dynamically by the component's input metadata schema.
502
+
503
+ ### 2. What the LLM Sends to the REST API (Request Body Payload)
504
+ Under the hood, the REST API does not have a separate "settings" and "mappings" screen. Instead, everything is serialized into a single JSON API `graph` payload:
505
+ * **User's Static Settings/Fields** are placed inside the **Node's `fields` object** (`nodes[i].fields`):
506
+ ```json
507
+ {
508
+ "id": "step_3",
509
+ "component_id": "salesforce-component-id",
510
+ "command": "upsertObject",
511
+ "fields": {
512
+ "object": "Account" // Static settings go in the node fields
513
+ }
514
+ }
515
+ ```
516
+ * **User's Input Mappings** are placed inside the **Edge's `config.mapper` object** (`edges[j].config.mapper`):
517
+ ```json
518
+ {
519
+ "id": "step_2:step_3",
520
+ "source": "step_2",
521
+ "target": "step_3",
522
+ "config": {
523
+ "mapper_type": "jsonata",
524
+ "mapper": "{\n \"Name\": $getPassthrough().step_2.body.company_name\n}" // Mappings go in the edge mapper, NEVER the node fields
525
+ }
526
+ }
527
+ ```
528
+
529
+ ## Content-Based Router (Branching)
530
+ The Content-Based Router component (`elasticio/router`, action `route`) examines message content and routes messages to different flow branches based on conditions.
531
+
532
+ > [!CAUTION]
533
+ > **Do NOT use the Router for single-branch filtering.** The Router component is only justified when there are **2 or more outgoing branches** with different conditions. If you only need to conditionally pass or drop messages, put the `condition` directly on the edge between the source and target steps — no Router node needed. A Router with a single outgoing edge is redundant and adds unnecessary complexity to the flow.
534
+
535
+ ### Router Node
536
+ The router node itself has no special fields — just set the `command` to `route`:
537
+ ```json
538
+ {
539
+ "id": "step_2",
540
+ "component_id": "<router_component_id>",
541
+ "command": "elasticio/router:route@<latestVersionId>",
542
+ "name": "Router",
543
+ "fields": {}
544
+ }
545
+ ```
546
+
547
+ ### Router Edge Conditions
548
+ Each outgoing edge from the router MUST have a `condition` property in its `config`. The condition is a JSONata expression that evaluates to `true` or `false`:
549
+ ```json
550
+ {
551
+ "id": "step_2:step_3",
552
+ "source": "step_2",
553
+ "target": "step_3",
554
+ "config": {
555
+ "condition": "$getPassthrough().step_1.body.status = \"paid\""
556
+ }
557
+ }
558
+ ```
559
+
560
+ If the edge also has a mapper, include both `condition` and `mapper`/`mapper_type` in `config`.
561
+
562
+ > [!WARNING]
563
+ > **Restricted JSONata Functions in Router Conditions**: Router conditions run in a restricted JSONata context. The following built-in JSONata functions are **NOT available** and will cause "Attempted to invoke a non-function" errors:
564
+ > - `$now()`, `$millis()` — time generation functions
565
+ > - `$hour()`, `$minute()`, `$second()`, `$year()`, `$month()`, `$day()` — time component extraction functions
566
+ > - `$toMillis()`, `$fromMillis()` — time conversion functions
567
+ > - `$formatTimestamp()`, `$parseTimestamp()`
568
+ >
569
+ > **Workaround**: Use `$substring()` to extract parts from ISO 8601 date strings available in passthrough data. For example, to extract the hour from a `fireTime` like `"2026-08-26T14:30:00.000Z"`:
570
+ > ```
571
+ > $substring($getPassthrough().step_1.body.fireTime, 11, 2) < "12"
572
+ > ```
573
+ >
574
+ > **Available functions** in conditions include: `$getPassthrough()`, `$substring()`, `$string()`, `$number()`, `$length()`, `$contains()`, `$split()`, `$join()`, `$lowercase()`, `$uppercase()`, `$trim()`, `$replace()`, `$match()`, and standard comparison/logic operators.
575
+
576
+ ## Abstract Flow Topology Templates (Few-Shot Reference)
577
+ When you need to construct a new flow:
578
+ 1. **Query Patterns**: Call `search_flow_samples` with semantic queries (e.g. "polling sync timer", "splitter loop", "webhook reply", "batch collect") to retrieve the matching abstract pattern topology.
579
+ 2. **Template Decoupling**: The retrieved templates contain generic dummy component IDs (e.g. `generic_source_component_id`), placeholder credentials (`TARGET_CREDENTIAL_ID_PLACEHOLDER`), and version hashes (`@latestVersionId`).
580
+ 3. **Resolve Real Properties**: Discover the actual component IDs by calling `list_components` and `get_component`, and discover credentials using `list_credentials`/`list_secrets`.
581
+ 4. **Swap and Connect**: Swap the real component IDs, credentials/secrets, commands, and fields into the abstract pattern layout, and map properties using standard JSONata mapping rules (`$getPassthrough().step_id.body.field`).
@@ -0,0 +1,58 @@
1
+ # Implementing Pagination with the REST API Component and Any Component That Uses the makeRawRequest Action
2
+
3
+ The REST API component's `httpRequestAxios` action (and the `makeRawRequest` action available in other components) does not provide automatic pagination or automatically retrieve subsequent pages. To retrieve all available records, implement pagination logic directly in your integration flow using a recursive flow execution pattern.
4
+
5
+ The pagination mechanism depends on the target REST API. It may provide a next-page URL, a cursor, a page number, an offset, or another pagination parameter. Always follow the pagination mechanism documented by the target API.
6
+
7
+ ## 1. Flow Linking: Receive pagination parameters
8
+
9
+ Configure a Flow Linking trigger to receive the input required to execute a paginated request.
10
+
11
+ The input should contain the initial request URL or parameters for the first execution. For subsequent executions, it should contain the pagination information extracted from the previous response.
12
+
13
+ ## 2. REST API: httpRequestAxios
14
+
15
+ Configure the REST API component (or any component that uses the `makeRawRequest` action) with the `httpRequestAxios` action to execute the HTTP request.
16
+
17
+ - Set the HTTP method, URL, headers, authentication, and other required request parameters.
18
+ - Use the initial URL or pagination parameters received from the Flow Linking trigger.
19
+ - Execute the request and retrieve the response.
20
+
21
+ The action provides the HTTP status code, response headers (`HTTPHeaders`), and response body (`responseBody`), unless the response is configured to be downloaded as an attachment.
22
+
23
+ ## 3. Filter or Router: Check whether another page is available
24
+
25
+ Inspect the response body and, if applicable, the response headers to determine whether more records are available.
26
+
27
+ Use the pagination mechanism supported by the target API. For example:
28
+
29
+ - **Next-page URL**: Check whether the response contains a next-page URL or link.
30
+ - **Cursor-based pagination**: Check whether the response contains a continuation cursor or token.
31
+ - **Offset-based pagination**: Check whether the API indicates that additional records are available and calculate the next offset.
32
+ - **Page-based pagination**: Check whether another page exists and increment the page number.
33
+
34
+ If another page is available, continue to the next step. Otherwise, stop pagination because the last page has been reached.
35
+
36
+ ## 4. Flow Linking: Trigger the next execution
37
+
38
+ If another page is available, use Flow Linking to trigger the same flow again.
39
+
40
+ Pass the pagination information extracted from the previous response, such as the next-page URL, cursor, offset, or page number.
41
+
42
+ The next execution must use this information to configure the subsequent REST API request.
43
+
44
+ ## 5. Repeat until all pages are retrieved
45
+
46
+ Repeat the request and pagination-checking steps until the target API indicates that no more pages are available.
47
+
48
+ Ensure that all retrieved records are processed or stored as required by the integration flow. If necessary, use additional steps to aggregate records from multiple responses.
49
+
50
+ ## Important considerations
51
+
52
+ - Do not assume that all REST APIs use the same pagination mechanism or response structure.
53
+ - Use the pagination parameters and continuation indicators documented by the target API.
54
+ - Do not construct or modify pagination URLs when the API provides a complete next-page URL. Follow the returned URL as specified by the API.
55
+ - Ensure that the pagination condition correctly identifies the last page to prevent unnecessary requests or infinite execution loops.
56
+ - Consider API rate limits and execution constraints when implementing recursive requests. Configure appropriate delays between requests when necessary.
57
+
58
+ This approach allows you to retrieve all available records from REST APIs that use pagination, even when automatic pagination is not supported by the REST API component or other components that use the `makeRawRequest` action.