@celigo/api-specs 0.2.132 → 0.2.134

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ai-agent.yml CHANGED
@@ -1113,7 +1113,6 @@ components:
1113
1113
  - gpt-5-mini
1114
1114
  - gpt-5-nano
1115
1115
  - gpt-4.1-mini
1116
- - gpt-4o
1117
1116
  x-celigo-ui-override: Required by the AI agent configuration form. Encoded here to mirror the form so builders produce connectable configurations.
1118
1117
  x-celigo-ai-guidance:
1119
1118
  - |-
@@ -5790,7 +5789,8 @@ components:
5790
5789
  - filter
5791
5790
  description: |-
5792
5791
  Filter applied to incoming records before they are sent to the LLM. Records that match
5793
- continue through the agent; records that don't are silently dropped.
5792
+ continue through the agent; records that don't skip it, counted as ignored with no model
5793
+ call, and continue to the next step.
5794
5794
  allOf:
5795
5795
  - $ref: '#/components/schemas/Filter'
5796
5796
  responseTransform:
package/dist/export.yml CHANGED
@@ -360,8 +360,9 @@ paths:
360
360
  x-internal: false
361
361
  summary: Clone an export
362
362
  description: |
363
- Creates a copy of an existing export.
364
- Supports optionally remapping referenced connections (via connectionMap).
363
+ Creates a copy of an existing export, remapping its connection through `connectionMap`.
364
+ For an export that uses a connection, omitting the map or sending an empty one fails with
365
+ 400 `invalid_connectionMap`; map the connection to itself to keep it.
365
366
  operationId: cloneExport
366
367
  tags:
367
368
  - Exports
@@ -500,7 +501,11 @@ paths:
500
501
  description: |-
501
502
  Full export document (mirror of `POST /v1/exports` body shape),
502
503
  optionally with `test.limit` to cap preview record count. Omit
503
- `_id` when previewing a not-yet-saved export.
504
+ `_id` when previewing a not-yet-saved export. For an export this
505
+ endpoint previews (an HTTP export, for example), a `mockOutput` in
506
+ the body is returned as a lone `parse` stage instead of a live read,
507
+ so remove it from a document read back with GET to sample the
508
+ source.
504
509
  additionalProperties: true
505
510
  examples:
506
511
  http_preview:
@@ -528,9 +533,9 @@ paths:
528
533
  '400':
529
534
  description: |-
530
535
  The export type does not support preview. Virtual (unsaved) previews
531
- are rejected for `simple` and other virtual export types — only
532
- builder-style exports (e.g. HTTP, RDBMS) can be previewed via this
533
- endpoint.
536
+ are rejected for `simple`, `webhook` and other virtual export types —
537
+ only builder-style exports (e.g. HTTP, RDBMS) can be previewed via
538
+ this endpoint.
534
539
  content:
535
540
  application/json:
536
541
  schema:
@@ -633,8 +638,9 @@ paths:
633
638
  validation (bad ID, missing auth).
634
639
 
635
640
  This endpoint executes the export against the live source system.
636
- `POST /v1/exports/preview` also queries the live source (reads only,
637
- no job) — prefer it when you only need to inspect fetched records.
641
+ `POST /v1/exports/preview` also queries the live source when its body
642
+ carries no `mockOutput` (reads only, no job) — prefer it when you only
643
+ need to inspect fetched records.
638
644
  parameters:
639
645
  - in: path
640
646
  name: _id
@@ -5485,7 +5491,7 @@ components:
5485
5491
  Selects which files are processed: files matching the filter criteria are exported,
5486
5492
  and non-matching files are skipped entirely before processing begins. Filterable
5487
5493
  fields come from each file's metadata — most providers expose filename, filesize,
5488
- and lastmodified (e.g. ["endswith", ["extract", "filename"], ".csv"]).
5494
+ and lastmodified (e.g. ["endswith", ["string", ["extract", "filename"]], ".csv"]).
5489
5495
  allOf:
5490
5496
  - $ref: '#/components/schemas/Filter'
5491
5497
  x-celigo-ai-guidance:
@@ -5500,7 +5506,7 @@ components:
5500
5506
 
5501
5507
  1. **filename**: The name of the file (with extension)
5502
5508
  - Example filter: Match files with specific extensions or naming patterns
5503
- - Usage: `["endswith", ["extract", "filename"], ".csv"]`
5509
+ - Usage: `["endswith", ["string", ["extract", "filename"]], ".csv"]`
5504
5510
 
5505
5511
  2. **filesize**: The size of the file in bytes
5506
5512
  - Example filter: Skip files that are too large or too small
@@ -5508,7 +5514,7 @@ components:
5508
5514
 
5509
5515
  3. **lastmodified**: The last modification timestamp of the file
5510
5516
  - Example filter: Process only files created/modified within a specific date range
5511
- - Usage: `["greaterthan", ["extract", "lastmodified"], "2023-01-01T00:00:00Z"]`
5517
+ - Usage: `["greaterthan", ["epochtime", ["extract", "lastmodified"]], 1672531200000]` (2023-01-01T00:00:00Z in epoch milliseconds)
5512
5518
  backupPath:
5513
5519
  type: string
5514
5520
  description: Path where backup copies of source files are stored before processing.
@@ -5769,38 +5775,10 @@ components:
5769
5775
  bearerToken:
5770
5776
  type: string
5771
5777
  description: |-
5772
- Bearer token used to authenticate inbound distributed export requests. Optional — most
5773
- distributed listeners (NetSuite, Salesforce real-time) use no token. Write-only and stored
5774
- encrypted; treat it as a sensitive credential.
5775
- x-celigo-ai-guidance:
5776
- - |-
5777
- ## FIELD BEHAVIOR
5778
-
5779
- This token provides authentication for the distributed export:
5780
-
5781
- - Required for secure access to distributed endpoints
5782
- - Must be a valid bearer token format
5783
- - Used in Authorization header as "Bearer {token}"
5784
- - Should be kept secure and rotated regularly
5785
- - |-
5786
- ## IMPLEMENTATION GUIDANCE
5787
-
5788
- ### Token management
5789
-
5790
- - Store tokens securely (encrypted at rest)
5791
- - Implement token rotation policies
5792
- - Monitor token expiration dates
5793
- - Use environment variables for token storage
5794
-
5795
- ### Security considerations
5796
-
5797
- - Never log bearer tokens in plain text
5798
- - Implement proper access controls
5799
- - Use HTTPS for all token transmissions
5800
- - Validate tokens on each request
5801
-
5802
- IMPORTANT: Bearer tokens provide full access to the distributed export.
5803
- Treat them as sensitive credentials.
5778
+ Bearer token that a system pushing data into this export sends as
5779
+ `Authorization: Bearer <token>`. Optional — most distributed listeners (NetSuite,
5780
+ Salesforce real-time) use no token. Write-only and stored encrypted; treat it as a
5781
+ sensitive credential.
5804
5782
  format: password
5805
5783
  examples:
5806
5784
  - eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
@@ -6196,7 +6174,7 @@ components:
6196
6174
  (`http.paging.body` likewise from its paging fields). A body that is not parseable JSON at
6197
6175
  rest — Handlebars block helpers outside a string value — renders every field EMPTY in that
6198
6176
  form, so keep templated variables inside a JSON string value:
6199
- `"variables": "{\"after\": {{#if export.http.paging.token}}\"{{export.http.paging.token}}\"{{else}}null{{/if}}}"`.
6177
+ `"variables": "{\"after\": {{#if export.http.paging.token}}\"{{export.http.paging.token}}\"{{else}}null{{/if}} }"`.
6200
6178
  - |-
6201
6179
  The GraphQL form has no relative URI field: the connection `baseURI` is expected to be the
6202
6180
  GraphQL endpoint (a saved `relativeURI` still applies but is not shown). Create GraphQL
@@ -15640,9 +15618,10 @@ components:
15640
15618
 
15641
15619
  In a flow test run, and for an upstream lookup during a page-processor preview
15642
15620
  (`previewPageProcessor`), integrator.io substitutes this mock output for the export's live
15643
- response instead of calling the source. It is not substituted anywhere else: a target lookup
15644
- in that preview runs its live query, `POST /v1/exports/preview` calls the source, and real
15645
- runs ignore it. This allows you to:
15621
+ response instead of calling the source. A saved mock is not read anywhere else: a target
15622
+ lookup in that preview runs its live query, invoking the export
15623
+ (`POST /v1/exports/{_id}/invoke`) reads the source, and real runs ignore it. This allows you
15624
+ to:
15646
15625
  - Test mappings with representative data
15647
15626
  - Configure downstream flow steps without waiting for real data
15648
15627
  - Simulate various data scenarios
@@ -15651,6 +15630,11 @@ components:
15651
15630
  `transform` and output `filter` still run on top of them, so downstream steps — and a
15652
15631
  lookup's results mapping — see the transformed, filtered records, not the shape stored here.
15653
15632
 
15633
+ `POST /v1/exports/preview` reads the `mockOutput` in its request body, not the saved one. For
15634
+ an export the preview supports (an HTTP export, for example), a body that carries it (as an
15635
+ export read back with GET does) returns that snapshot as a lone `parse` stage without calling
15636
+ the source; remove `mockOutput` from the body to preview live records.
15637
+
15654
15638
  **Limitations**
15655
15639
 
15656
15640
  - Maximum of 10 records
@@ -15668,8 +15652,8 @@ components:
15668
15652
  the stored shape walks paths that do not exist at runtime.
15669
15653
  - |-
15670
15654
  A saved mockOutput is read in exactly two places — a flow test run, and an upstream lookup
15671
- during a page-processor preview. A target lookup's preview, `POST /v1/exports/preview` and
15672
- every real run call the source and never read it.
15655
+ during a page-processor preview. A target lookup's preview, `POST /v1/exports/{_id}/invoke`
15656
+ and every real run call the source and never read it.
15673
15657
  properties:
15674
15658
  page_of_records:
15675
15659
  type: array
@@ -16181,9 +16165,9 @@ components:
16181
16165
  x-celigo-agent:
16182
16166
  - filter
16183
16167
  description: |-
16184
- Filter applied to records immediately after they are retrieved from the source.
16185
- Records that match continue through the flow; records that don't are silently dropped.
16186
- Filter expressions reference the exported record's own fields.
16168
+ Filter applied to each record after the export's transform and before its preSavePage
16169
+ hook. Records that match continue through the flow; records that don't are silently
16170
+ dropped. Filter expressions reference the transformed record's fields.
16187
16171
  allOf:
16188
16172
  - $ref: '#/components/schemas/Filter'
16189
16173
  x-celigo-ai-guidance:
@@ -16193,13 +16177,13 @@ components:
16193
16177
 
16194
16178
  ## FILTER BEHAVIOR
16195
16179
 
16196
- When configured, the filter is applied immediately after records are retrieved from the source system:
16180
+ When configured, the filter is applied to each record after the export's transform and before the preSavePage hook:
16197
16181
  - Records that match the filter criteria continue through the flow
16198
16182
  - Records that don't match are silently dropped
16199
16183
  - No partial record processing is performed
16200
16184
 
16201
16185
  ## AVAILABLE FILTER FIELDS
16202
- The fields available for filtering are the data fields from each record retrieved by the export.
16186
+ The fields available for filtering are those of each record as the export's transform leaves it (the retrieved record when no transform is set).
16203
16187
  inputFilter:
16204
16188
  x-celigo-agent:
16205
16189
  - filter
@@ -16312,9 +16296,12 @@ components:
16312
16296
  x-celigo-agent:
16313
16297
  - script
16314
16298
  description: |-
16315
- Hook that runs after each page of records is retrieved from the source but before it
16316
- is sent to downstream steps. Commonly used to flatten structures, drop unwanted
16317
- records, or add computed fields.
16299
+ Hook that runs on each page of records after the export's transform and output
16300
+ filter, before the page is sent to downstream steps; commonly used to flatten
16301
+ structures, drop unwanted records, or add computed fields. It receives the page as
16302
+ `options.data` (with `errors`, `pageIndex`, `configuration` and `settings`) and returns
16303
+ `{data, errors, abort, newErrorsAndRetryData}`. An exception thrown here fails every
16304
+ record on that page, each with its own retry data, and later pages still run.
16318
16305
  properties:
16319
16306
  function:
16320
16307
  type: string
@@ -17114,8 +17101,10 @@ components:
17114
17101
  connectionMap:
17115
17102
  type: object
17116
17103
  description: |
17117
- Optional mapping of original connection ids to replacement connection ids.
17104
+ Mapping of original connection ids to replacement connection ids.
17118
17105
  Keys are source connection ids on the original resource; values are target connection ids.
17106
+ For an export that uses a connection, omitting the map or sending an empty one fails
17107
+ with 400 `invalid_connectionMap`; map the connection to itself to reuse it in the clone.
17119
17108
  additionalProperties:
17120
17109
  type: string
17121
17110
  example:
@@ -17155,6 +17144,7 @@ components:
17155
17144
  Final sampled records produced by the preview — the output of the last
17156
17145
  stage. Identical in practice to the last `stages[].data` entry, exposed
17157
17146
  here as a convenience for callers that only care about the end result.
17147
+ Absent from the zero-record answers described under `stages`.
17158
17148
  items: {}
17159
17149
  dataURIs:
17160
17150
  type: array
@@ -17177,10 +17167,15 @@ components:
17177
17167
  runs through parsing only — the export's transform, output filter,
17178
17168
  and preSavePage hook are NOT applied here (they run in flow
17179
17169
  previews, test runs, and real runs). Observed stage names: `parse`
17180
- always; `request` and `raw` for HTTP exports; `group` when the
17181
- export groups records (grouped records are bare arrays of the
17182
- grouped rows). Each stage carries its `data[]` output and any
17183
- `errors[]` raised at that stage.
17170
+ for a live read that returned records, and as the only stage when a
17171
+ `mockOutput` in the request body answers; `request` and `raw` for HTTP
17172
+ exports; `group` when the export groups records (grouped records are
17173
+ bare arrays of the grouped rows). Each stage carries its `data[]`
17174
+ output and any `errors[]` raised at that stage. A live read that
17175
+ selects no records has been observed without a `parse` stage or
17176
+ top-level `data` on two adaptors: an HTTP export returns only `request`
17177
+ and `raw`, and a NetSuite RESTlet search returns an empty object. Treat
17178
+ those as zero records, not as a failed preview.
17184
17179
  items:
17185
17180
  type: object
17186
17181
  description: One pipeline stage's diagnostic and output envelope.
package/dist/flow.yml CHANGED
@@ -8249,6 +8249,7 @@ components:
8249
8249
  Final sampled records produced by the preview — the output of the last
8250
8250
  stage. Identical in practice to the last `stages[].data` entry, exposed
8251
8251
  here as a convenience for callers that only care about the end result.
8252
+ Absent from the zero-record answers described under `stages`.
8252
8253
  items: {}
8253
8254
  dataURIs:
8254
8255
  type: array
@@ -8271,10 +8272,15 @@ components:
8271
8272
  runs through parsing only — the export's transform, output filter,
8272
8273
  and preSavePage hook are NOT applied here (they run in flow
8273
8274
  previews, test runs, and real runs). Observed stage names: `parse`
8274
- always; `request` and `raw` for HTTP exports; `group` when the
8275
- export groups records (grouped records are bare arrays of the
8276
- grouped rows). Each stage carries its `data[]` output and any
8277
- `errors[]` raised at that stage.
8275
+ for a live read that returned records, and as the only stage when a
8276
+ `mockOutput` in the request body answers; `request` and `raw` for HTTP
8277
+ exports; `group` when the export groups records (grouped records are
8278
+ bare arrays of the grouped rows). Each stage carries its `data[]`
8279
+ output and any `errors[]` raised at that stage. A live read that
8280
+ selects no records has been observed without a `parse` stage or
8281
+ top-level `data` on two adaptors: an HTTP export returns only `request`
8282
+ and `raw`, and a NetSuite RESTlet search returns an empty object. Treat
8283
+ those as zero records, not as a failed preview.
8278
8284
  items:
8279
8285
  type: object
8280
8286
  description: One pipeline stage's diagnostic and output envelope.
package/dist/import.yml CHANGED
@@ -372,8 +372,9 @@ paths:
372
372
  x-internal: false
373
373
  summary: Clone an import
374
374
  description: |
375
- Creates a copy of an existing import.
376
- Supports optionally remapping referenced connections (via connectionMap).
375
+ Creates a copy of an existing import, remapping its connection through `connectionMap`.
376
+ For an import that uses a connection, an empty map fails with 400 `invalid_connectionMap`;
377
+ map the connection to itself to keep it.
377
378
  operationId: cloneImport
378
379
  tags:
379
380
  - Imports
@@ -549,6 +550,10 @@ paths:
549
550
  composed, not delivered — an FTP import against an unreachable host
550
551
  still returns `{name, fileLocation}`.
551
552
 
553
+ The import runs with its own `settings` only: hooks receive
554
+ `options.settings` with an `import` key but no `integration` or `flow`
555
+ key, and no `_flowId` or `_integrationId`.
556
+
552
557
  The import must be saved; to run an import definition that is not saved
553
558
  (or a modified copy of one), use the virtual import
554
559
  `POST /v1/connections/{_id}/import`, which takes the import document in
@@ -1303,7 +1308,7 @@ components:
1303
1308
  ## EXAMPLES:
1304
1309
  - `{ "UserId": "12345", "Name": "John Doe", "Age": 30, "Preferences": { "Language": "en", "Notifications": true } }`
1305
1310
  examples:
1306
- - '{"id": "{{record.id}}", "name": "{{record.name}}", "total": {{record.total}}}'
1311
+ - '{"id": "{{record.id}}", "name": "{{record.name}}", "total": {{record.total}} }'
1307
1312
  updateExpression:
1308
1313
  type: string
1309
1314
  x-celigo-ui-override: Required by the DynamoDB import form when method is updateItem (dynamodb.updateExpression required, visibleWhen method=updateItem).
@@ -1966,7 +1971,10 @@ components:
1966
1971
  the Import's mapping step runs first and shapes the source record,
1967
1972
  then this template renders against that mapped record.
1968
1973
  Reference the mapped record with the ``record`` root when wrapping it (e.g.
1969
- ``{"requestData": {{{jsonSerialize record}}}}``).
1974
+ ``{"requestData": {{{jsonSerialize record}}} }``).
1975
+ Keep a literal ``}`` apart from a closing ``}}`` or ``}}}`` (a space is enough): the save
1976
+ accepts ``{{{jsonSerialize record}}}}``, but every record then fails with
1977
+ ``handlebars_template_parse_error``.
1970
1978
  Avoid bare ``{{{jsonSerialize record}}}`` with no wrapper — leave the field undefined instead.
1971
1979
  Also avoid wrapping a single post-mapped scalar field in ``jsonSerialize`` (e.g.
1972
1980
  ``{{{jsonSerialize record.<scalar>}}}``) — that pattern is brittle.
@@ -2029,9 +2037,9 @@ components:
2029
2037
 
2030
2038
  Typical shapes:
2031
2039
  - Message-bus envelope:
2032
- ``{"MessageBody": {"class": "EntityUpdate", "payload": {{{jsonSerialize record}}}}}``
2040
+ ``{"MessageBody": {"class": "EntityUpdate", "payload": {{{jsonSerialize record}}} }}``
2033
2041
  - Metadata wrap:
2034
- ``{"batchNumber": "{{timestamp dateTimeFormat CST}}", "interfaceType": "...", "requestData": {{{jsonSerialize record}}}}``
2042
+ ``{"batchNumber": "{{timestamp dateTimeFormat CST}}", "interfaceType": "...", "requestData": {{{jsonSerialize record}}} }``
2035
2043
  - Named single-record array wrap (when destination wants ``[record]`` but each request sends one):
2036
2044
  ``{"products": [{{{jsonSerialize record}}}]}``
2037
2045
 
@@ -2072,7 +2080,7 @@ components:
2072
2080
  current ``{{#each data}}`` item.
2073
2081
 
2074
2082
  Example shape:
2075
- ``{"header": {"sellerId": "{{connection.http.unencrypted.sellerId}}"}, "messages": [{{#each data}}{"sku": "{{sku}}", "qty": {{quantity}}}{{/each}}]}``
2083
+ ``{"header": {"sellerId": "{{connection.http.unencrypted.sellerId}}"}, "messages": [{{#each data}}{"sku": "{{sku}}", "qty": {{quantity}} }{{/each}}]}``
2076
2084
  - |-
2077
2085
  ## Anti-pattern — DO NOT wrap a post-mapped scalar in jsonSerialize
2078
2086
 
@@ -2157,7 +2165,7 @@ components:
2157
2165
  mix the two shapes in one body.
2158
2166
  examples:
2159
2167
  - - '{"name": "{{record.name}}", "email": "{{record.email}}"}'
2160
- - - '{"requestData": {{{jsonSerialize record}}}}'
2168
+ - - '{"requestData": {{{jsonSerialize record}}} }'
2161
2169
  - - '{"values": [{{#each batch_of_records}}{{{jsonSerialize this.record}}}{{#unless @last}},{{/unless}}{{/each}}]}'
2162
2170
  x-celigo-agent:
2163
2171
  - handlebars
@@ -2647,9 +2655,7 @@ components:
2647
2655
 
2648
2656
  The referenced helper bundles the polling config plus a required status
2649
2657
  export (polled to check progress) and a result export (fetches the
2650
- final payload). An import configured with an async helper cannot carry
2651
- its own transform, output filter, or preSavePage hook — put that
2652
- processing in the result export instead.
2658
+ final payload).
2653
2659
  examples:
2654
2660
  - 60a2c4e6f321d800129a1a3c
2655
2661
  x-celigo-agent:
@@ -2733,7 +2739,7 @@ components:
2733
2739
 
2734
2740
  - **Static** — only ``map`` + optional ``default``. No HTTP
2735
2741
  request issued. Use for small controlled vocabularies.
2736
- - **Dynamic** — ``method`` + ``relativeURI`` (+ ``postBody``,
2742
+ - **Dynamic** — ``method`` + ``relativeURI`` (+ ``body``,
2737
2743
  ``headers``, ``extract``). Issues a live HTTP request per
2738
2744
  lookup. Adds one HTTP request per record at runtime — factor
2739
2745
  this into flow design for high-volume syncs.
@@ -2771,14 +2777,18 @@ components:
2771
2777
  inject values from the incoming record — e.g.
2772
2778
  ``/customers?email={{{email}}}`` or
2773
2779
  ``/accounts/{{{accountId}}}``.
2774
- postBody:
2780
+ body:
2775
2781
  type: string
2776
2782
  x-celigo-agent:
2777
2783
  - handlebars
2778
2784
  description: |
2779
- Request body template for POST / PUT / PATCH lookup methods.
2780
- Typically a JSON string with Handlebars placeholders —
2781
- ``{"email":"{{{email}}}"}``. Ignored for GET / DELETE.
2785
+ Request body template for POST / PUT / PATCH lookup methods, typically
2786
+ a JSON string with Handlebars placeholders (``{"email":"{{{email}}}"}``);
2787
+ ignored for GET / DELETE. The record's fields sit at the top level:
2788
+ ``{{email}}`` renders the record's value, while ``{{record.email}}``
2789
+ renders empty.
2790
+ x-celigo-ai-guidance:
2791
+ - 'Put the lookup''s request template here, not in ``postBody``: a ``postBody`` saves without an error but is not kept under ``http.lookups``, so the lookup goes out with no body and the run reports no error.'
2782
2792
  headers:
2783
2793
  type: array
2784
2794
  items:
@@ -4429,9 +4439,9 @@ components:
4429
4439
  line) to the rows that populate the
4430
4440
  subrecord. Typically `$` when the
4431
4441
  subrecord data is carried on the same
4432
- line item (the enclosing
4433
- `lists[].jsonPath` has already selected
4434
- the line). Defaults to `$`.
4442
+ line item (iteration over the sublist
4443
+ has already selected the line).
4444
+ Defaults to `$`.
4435
4445
  mapping:
4436
4446
  type: object
4437
4447
  description: |
@@ -5155,9 +5165,10 @@ components:
5155
5165
  SQL `SELECT` statement that resolves the lookup value.
5156
5166
  Typically returns a single row with a single column; use
5157
5167
  `extract` to pick the column when the query returns multiple.
5158
- Inject values from the incoming record with Handlebars — RDBMS
5159
- queries use triple braces and the `record.` prefix:
5160
- `SELECT id FROM users WHERE email = '{{{record.email}}}'`.
5168
+ Inject values from the incoming record with Handlebars and the
5169
+ `record.` prefix — double braces for strings, which the platform
5170
+ quotes and escapes, and triple braces for numbers:
5171
+ `SELECT id FROM users WHERE email = {{record.email}}`.
5161
5172
  extract:
5162
5173
  type: string
5163
5174
  description: |
@@ -5205,7 +5216,7 @@ components:
5205
5216
  description: |-
5206
5217
  SQL statements to execute; required when queryType is `["per_record"]`, `["per_page"]`, or `["first_page"]`, and typically contains a single statement.
5207
5218
  Each element is a complete SQL statement as a plain string — never a single bare string and never an array of objects (objects cause a Cast error).
5208
- Inject values from the incoming record with Handlebars using the `record.` prefix and triple braces, e.g. `'{{{record.name}}}'` for strings (single-quoted in the SQL) and `{{{record.quantity}}}` for numbers.
5219
+ Inject values from the incoming record with Handlebars using the `record.` prefix: double braces for strings and dates (`{{record.name}}`; the platform quotes and escapes the value) and triple braces for numbers (`{{{record.quantity}}}`).
5209
5220
  Rendering is strict: a reference the render model cannot resolve (a bare `{{name}}` without prefix, or `record.*` when the arriving record is a grouped array) fails the step with `cannot_evaluate_handlebars` — it does not render an empty value.
5210
5221
  `record` and `rows` are aliases chosen by the arriving data's shape: an object record binds `record` (`rows` undefined); a grouped array-record (e.g. upstream `groupByFields`) binds `rows` (`record` undefined) — address grouped rows as `{{rows.0.field}}` or iterate `{{#each rows}}`.
5211
5222
  Double braces (`{{record.field}}`) output the value wrapped in single quotes with embedded quotes doubled, while triple braces output it raw; block helpers such as `{{#each}}` and `{{#if}}` use double braces as normal.
@@ -5219,7 +5230,7 @@ components:
5219
5230
  - CORRECT: ["INSERT INTO users (name, email) VALUES ({{record.name}}, {{record.email}})"]
5220
5231
  - WRONG: "INSERT INTO users ..." (not an array)
5221
5232
  - WRONG: [{"query": "INSERT INTO users ..."}] (objects are invalid — causes Cast error)
5222
- - WRONG: ["INSERT INTO users (name) VALUES ('{{name}}')"] (missing record. prefix — rendering is strict, so the step fails with cannot_evaluate_handlebars)
5233
+ - WRONG: ["INSERT INTO users (name) VALUES ({{name}})"] (missing record. prefix — rendering is strict, so the step fails with cannot_evaluate_handlebars)
5223
5234
  - |-
5224
5235
  ## RDBMS HANDLEBARS SYNTAX
5225
5236
 
@@ -5253,7 +5264,7 @@ components:
5253
5264
  | Numeric column value (INT, DECIMAL, ...) | `{{{record.field}}}` |
5254
5265
  | Stored procedure raw arg (already typed) | `{{{record.field}}}` |
5255
5266
  | Identifier (table/column name) | `{{{record.field}}}` |
5256
- | JSON literal (`PARSE_JSON`/JSONB) | `{{{jsonSerialize record.subobject}}}` |
5267
+ | JSON literal (`PARSE_JSON`/JSONB) | `{{jsonSerialize record.subobject}}` (renders the JSON as a quoted string) |
5257
5268
 
5258
5269
  - **Block helpers** (`{{#each}}`, `{{#if}}`, `{{/each}}`,
5259
5270
  `{{#unless}}`) use double braces as normal — they emit no value.
@@ -5272,7 +5283,7 @@ components:
5272
5283
  - Per-page batch (different Handlebars context — see `queryType: per_page` below): `["{{#each batch_of_records}}UPDATE staging SET done=1 WHERE id={{{this.record.id}}};{{/each}}"]`
5273
5284
  examples:
5274
5285
  - - INSERT INTO orders (id, total) VALUES ({{{record.id}}}, {{{record.total}}})
5275
- - - '{{#each batch_of_records}}UPDATE customers SET name=''{{{record.name}}}'' WHERE id={{{record.id}}};{{/each}}'
5286
+ - - '{{#each batch_of_records}}UPDATE customers SET name={{record.name}} WHERE id={{{record.id}}};{{/each}}'
5276
5287
  queryType:
5277
5288
  type:
5278
5289
  - array
@@ -5301,7 +5312,7 @@ components:
5301
5312
  Prefer the highest-performance type the operation supports: bulk_load (Snowflake, Google BigQuery, Microsoft SQL Server on a cloud connection, Azure Synapse and Amazon Redshift — handles INSERT, upsert via `bulkLoad.primaryKeys`, and custom merge or ignore-existing logic via `bulkLoad.overrideMergeQuery`), then bulk_insert for pure INSERTs, then per_page, then per_record only when the logic cannot be expressed as a batch operation.
5302
5313
  For UPDATE, UPSERT, or ignore-existing logic on databases without bulk_load support, use per_page or per_record with `query` — bulk_insert has no duplicate-checking logic.
5303
5314
  Set a single value; do not combine types or use the legacy INSERT and UPDATE values.
5304
- per_page statements render against the whole batch (`batch_of_records`) rather than a single record — loop with `{{#each batch_of_records}}...{{{record.fieldName}}}...{{/each}}`.
5315
+ per_page statements render against the whole batch (`batch_of_records`) rather than a single record — loop with `{{#each batch_of_records}}...{{record.fieldName}}...{{/each}}`, with the same quoting as `query` (double braces for strings, triple for numbers).
5305
5316
 
5306
5317
  `bulk_load` — Stages the records as files and loads them with the database's native COPY / bulk mechanism for maximum throughput; supports INSERT, upsert via bulkLoad.primaryKeys, and custom merge or ignore-existing logic via bulkLoad.overrideMergeQuery; available on Snowflake, Google BigQuery, Microsoft SQL Server (cloud connections), Azure Synapse and Amazon Redshift (NetSuite Analytics Warehouse has it through the JDBC import). The import form calls it "Use optimized bulk load (recommended for larger imports)" and, beside it, calls bulk_insert "Use batch insert SQL query".
5307
5318
  `bulk_insert` — Batch INSERT via multi-row VALUES clause for efficient bulk data loading; has no upsert or ignore-existing logic and is available for all RDBMS types.
@@ -5348,7 +5359,7 @@ components:
5348
5359
  Prompt: "Import customers, ignore existing based on email"
5349
5360
  ```json
5350
5361
  "queryType": ["per_record"],
5351
- "query": ["INSERT INTO customers (name, email) VALUES ('{{{record.name}}}', '{{{record.email}}}')"]
5362
+ "query": ["INSERT INTO customers (name, email) VALUES ({{record.name}}, {{record.email}})"]
5352
5363
  ```
5353
5364
 
5354
5365
  ### Pure INSERT (no duplicate checking)
@@ -5362,7 +5373,7 @@ components:
5362
5373
  Prompt: "Update inventory counts"
5363
5374
  ```json
5364
5375
  "queryType": ["per_record"],
5365
- "query": ["UPDATE inventory SET qty = {{{record.qty}}} WHERE sku = '{{{record.sku}}}'"]
5376
+ "query": ["UPDATE inventory SET qty = {{{record.qty}}} WHERE sku = {{record.sku}}"]
5366
5377
  ```
5367
5378
 
5368
5379
  ### Run Once Per Page
@@ -5370,7 +5381,7 @@ components:
5370
5381
  ```json
5371
5382
  "queryType": ["per_page"]
5372
5383
  ```
5373
- **per_page uses a different Handlebars context than per_record.** The context is the entire batch (`batch_of_records`), not a single `record.` object. Loop through with `{{#each batch_of_records}}...{{{record.fieldName}}}...{{/each}}`.
5384
+ **per_page uses a different Handlebars context than per_record.** The context is the entire batch (`batch_of_records`), not a single `record.` object. Loop through with `{{#each batch_of_records}}...{{record.fieldName}}...{{/each}}`, quoting as in per_record: double braces for strings, triple braces for numbers.
5374
5385
 
5375
5386
  ### Run Once at Start
5376
5387
  Prompt: "Run this cleanup script before processing"
@@ -8330,8 +8341,8 @@ components:
8330
8341
  description: |-
8331
8342
  Configuration for a `ToolImport`, which executes a reusable Celigo Tool resource as the
8332
8343
  import action. The referenced tool defines its own input contract, processing pipeline, and
8333
- output; this object binds the tool to the import and optionally overrides the connections the
8334
- tool uses for this invocation.
8344
+ output; this object binds the tool to the import, along with the connections the tool's steps
8345
+ use for this invocation.
8335
8346
  x-celigo-ai-guidance:
8336
8347
  - |-
8337
8348
  A ToolImport is the **wrapper** that lets a flow / API / another Tool consume a
@@ -8382,7 +8393,8 @@ components:
8382
8393
  Per-import overrides applied to the referenced tool at execution time. Tools use a
8383
8394
  bring-your-own-keys connection model: the tool declares abstract connection slots, and the
8384
8395
  consumer binds concrete connections here so one tool definition can run against different
8385
- systems without being modified. Omit to run the tool with its default connections.
8396
+ systems without being modified. Bind every connection the tool's steps use; to keep the
8397
+ connection a step was authored against, bind it to itself.
8386
8398
  x-celigo-ai-guidance:
8387
8399
  - |-
8388
8400
  Per-consumer overrides applied when the wrapper invokes the underlying Tool.
@@ -8390,10 +8402,10 @@ components:
8390
8402
  imports each reference a `_connectionId`,
8391
8403
  and consumers bind real account connections to those abstract requirements here.
8392
8404
  - |-
8393
- Omit this block (or omit `connections` inside it) when the wrapper inherits the
8394
- Tool's authored connections as-is.
8395
- That's the right default when the Tool was authored against connections the
8396
- consumer also wants to use.
8405
+ The save accepts a wrapper without bindings, but when the Tool's steps use connections
8406
+ its run then fails with `connection_not_overridden`. Write one `connections` entry per
8407
+ connection the Tool's steps use, even when the wrapper keeps the Tool's authored
8408
+ connection (`_id` equal to `_abstractId`); a Tool whose steps use no connection needs none.
8397
8409
  properties:
8398
8410
  connections:
8399
8411
  type: array
@@ -9461,7 +9473,6 @@ components:
9461
9473
  - gpt-5-mini
9462
9474
  - gpt-5-nano
9463
9475
  - gpt-4.1-mini
9464
- - gpt-4o
9465
9476
  x-celigo-ui-override: Required by the AI agent configuration form. Encoded here to mirror the form so builders produce connectable configurations.
9466
9477
  x-celigo-ai-guidance:
9467
9478
  - |-
@@ -16188,15 +16199,16 @@ components:
16188
16199
  - filter
16189
16200
  description: |-
16190
16201
  Filter applied to incoming records before they are sent to the destination system.
16191
- Records that match continue through the import; records that don't are silently
16192
- dropped. Filter expressions reference the incoming record's fields.
16202
+ Records that match continue through the import; records that don't skip this import,
16203
+ counted as ignored, and continue to the next step. Filter expressions reference the
16204
+ incoming record's fields.
16193
16205
  allOf:
16194
16206
  - $ref: '#/components/schemas/Filter'
16195
16207
  x-celigo-ai-guidance:
16196
16208
  - |-
16197
16209
  ## IMPORT-SPECIFIC BEHAVIOR
16198
16210
 
16199
- **Pre-Import Filtering**: Filters are applied to incoming records before they are sent to the destination system. Records that don't match the filter criteria are silently dropped and not imported.
16211
+ **Pre-Import Filtering**: Filters are applied to incoming records before they are sent to the destination system. A record that doesn't match is not sent, but it is not removed from the flow: it continues to the next step without this import's response-mapped fields, so a later step that writes those fields needs its own filter.
16200
16212
  traceKeyTemplate:
16201
16213
  type: string
16202
16214
  x-celigo-agent:
@@ -16313,7 +16325,7 @@ components:
16313
16325
  mapping step, on the raw upstream records. Engage when the
16314
16326
  user needs to reshape, validate, filter, or annotate records
16315
16327
  before the mapping sees them (drop archived records,
16316
- normalize date strings, split a record into many). Don't
16328
+ normalize date strings). Don't
16317
16329
  engage for static value substitution or simple field
16318
16330
  renames — mappings handle those.
16319
16331
 
@@ -16589,7 +16601,7 @@ components:
16589
16601
  resultField, ...)
16590
16602
  - ``netsuite_da.lookups`` — NetSuite search (recordType,
16591
16603
  searchField, operator, ...)
16592
- - ``http.lookups`` — HTTP request (method, relativeURI, postBody,
16604
+ - ``http.lookups`` — HTTP request (method, relativeURI, body,
16593
16605
  extract, ...)
16594
16606
  - ``jdbc.lookups`` / ``rdbms.lookups`` — SQL query (query,
16595
16607
  extract, ...)
@@ -17125,8 +17137,11 @@ components:
17125
17137
  connectionMap:
17126
17138
  type: object
17127
17139
  description: |
17128
- Optional mapping of original connection ids to replacement connection ids.
17140
+ Mapping of original connection ids to replacement connection ids.
17129
17141
  Keys are source connection ids on the original resource; values are target connection ids.
17142
+ For an import that uses a connection, an empty map fails with 400
17143
+ `invalid_connectionMap`, as it does for an export; map the connection to itself to reuse
17144
+ it in the clone.
17130
17145
  additionalProperties:
17131
17146
  type: string
17132
17147
  example:
package/dist/tool.yml CHANGED
@@ -12883,7 +12883,7 @@ components:
12883
12883
  ## EXAMPLES:
12884
12884
  - `{ "UserId": "12345", "Name": "John Doe", "Age": 30, "Preferences": { "Language": "en", "Notifications": true } }`
12885
12885
  examples:
12886
- - '{"id": "{{record.id}}", "name": "{{record.name}}", "total": {{record.total}}}'
12886
+ - '{"id": "{{record.id}}", "name": "{{record.name}}", "total": {{record.total}} }'
12887
12887
  updateExpression:
12888
12888
  type: string
12889
12889
  x-celigo-ui-override: Required by the DynamoDB import form when method is updateItem (dynamodb.updateExpression required, visibleWhen method=updateItem).
@@ -13546,7 +13546,10 @@ components:
13546
13546
  the Import's mapping step runs first and shapes the source record,
13547
13547
  then this template renders against that mapped record.
13548
13548
  Reference the mapped record with the ``record`` root when wrapping it (e.g.
13549
- ``{"requestData": {{{jsonSerialize record}}}}``).
13549
+ ``{"requestData": {{{jsonSerialize record}}} }``).
13550
+ Keep a literal ``}`` apart from a closing ``}}`` or ``}}}`` (a space is enough): the save
13551
+ accepts ``{{{jsonSerialize record}}}}``, but every record then fails with
13552
+ ``handlebars_template_parse_error``.
13550
13553
  Avoid bare ``{{{jsonSerialize record}}}`` with no wrapper — leave the field undefined instead.
13551
13554
  Also avoid wrapping a single post-mapped scalar field in ``jsonSerialize`` (e.g.
13552
13555
  ``{{{jsonSerialize record.<scalar>}}}``) — that pattern is brittle.
@@ -13609,9 +13612,9 @@ components:
13609
13612
 
13610
13613
  Typical shapes:
13611
13614
  - Message-bus envelope:
13612
- ``{"MessageBody": {"class": "EntityUpdate", "payload": {{{jsonSerialize record}}}}}``
13615
+ ``{"MessageBody": {"class": "EntityUpdate", "payload": {{{jsonSerialize record}}} }}``
13613
13616
  - Metadata wrap:
13614
- ``{"batchNumber": "{{timestamp dateTimeFormat CST}}", "interfaceType": "...", "requestData": {{{jsonSerialize record}}}}``
13617
+ ``{"batchNumber": "{{timestamp dateTimeFormat CST}}", "interfaceType": "...", "requestData": {{{jsonSerialize record}}} }``
13615
13618
  - Named single-record array wrap (when destination wants ``[record]`` but each request sends one):
13616
13619
  ``{"products": [{{{jsonSerialize record}}}]}``
13617
13620
 
@@ -13652,7 +13655,7 @@ components:
13652
13655
  current ``{{#each data}}`` item.
13653
13656
 
13654
13657
  Example shape:
13655
- ``{"header": {"sellerId": "{{connection.http.unencrypted.sellerId}}"}, "messages": [{{#each data}}{"sku": "{{sku}}", "qty": {{quantity}}}{{/each}}]}``
13658
+ ``{"header": {"sellerId": "{{connection.http.unencrypted.sellerId}}"}, "messages": [{{#each data}}{"sku": "{{sku}}", "qty": {{quantity}} }{{/each}}]}``
13656
13659
  - |-
13657
13660
  ## Anti-pattern — DO NOT wrap a post-mapped scalar in jsonSerialize
13658
13661
 
@@ -13737,7 +13740,7 @@ components:
13737
13740
  mix the two shapes in one body.
13738
13741
  examples:
13739
13742
  - - '{"name": "{{record.name}}", "email": "{{record.email}}"}'
13740
- - - '{"requestData": {{{jsonSerialize record}}}}'
13743
+ - - '{"requestData": {{{jsonSerialize record}}} }'
13741
13744
  - - '{"values": [{{#each batch_of_records}}{{{jsonSerialize this.record}}}{{#unless @last}},{{/unless}}{{/each}}]}'
13742
13745
  x-celigo-agent:
13743
13746
  - handlebars
@@ -14227,9 +14230,7 @@ components:
14227
14230
 
14228
14231
  The referenced helper bundles the polling config plus a required status
14229
14232
  export (polled to check progress) and a result export (fetches the
14230
- final payload). An import configured with an async helper cannot carry
14231
- its own transform, output filter, or preSavePage hook — put that
14232
- processing in the result export instead.
14233
+ final payload).
14233
14234
  examples:
14234
14235
  - 60a2c4e6f321d800129a1a3c
14235
14236
  x-celigo-agent:
@@ -14313,7 +14314,7 @@ components:
14313
14314
 
14314
14315
  - **Static** — only ``map`` + optional ``default``. No HTTP
14315
14316
  request issued. Use for small controlled vocabularies.
14316
- - **Dynamic** — ``method`` + ``relativeURI`` (+ ``postBody``,
14317
+ - **Dynamic** — ``method`` + ``relativeURI`` (+ ``body``,
14317
14318
  ``headers``, ``extract``). Issues a live HTTP request per
14318
14319
  lookup. Adds one HTTP request per record at runtime — factor
14319
14320
  this into flow design for high-volume syncs.
@@ -14351,14 +14352,18 @@ components:
14351
14352
  inject values from the incoming record — e.g.
14352
14353
  ``/customers?email={{{email}}}`` or
14353
14354
  ``/accounts/{{{accountId}}}``.
14354
- postBody:
14355
+ body:
14355
14356
  type: string
14356
14357
  x-celigo-agent:
14357
14358
  - handlebars
14358
14359
  description: |
14359
- Request body template for POST / PUT / PATCH lookup methods.
14360
- Typically a JSON string with Handlebars placeholders —
14361
- ``{"email":"{{{email}}}"}``. Ignored for GET / DELETE.
14360
+ Request body template for POST / PUT / PATCH lookup methods, typically
14361
+ a JSON string with Handlebars placeholders (``{"email":"{{{email}}}"}``);
14362
+ ignored for GET / DELETE. The record's fields sit at the top level:
14363
+ ``{{email}}`` renders the record's value, while ``{{record.email}}``
14364
+ renders empty.
14365
+ x-celigo-ai-guidance:
14366
+ - 'Put the lookup''s request template here, not in ``postBody``: a ``postBody`` saves without an error but is not kept under ``http.lookups``, so the lookup goes out with no body and the run reports no error.'
14362
14367
  headers:
14363
14368
  type: array
14364
14369
  items:
@@ -16009,9 +16014,9 @@ components:
16009
16014
  line) to the rows that populate the
16010
16015
  subrecord. Typically `$` when the
16011
16016
  subrecord data is carried on the same
16012
- line item (the enclosing
16013
- `lists[].jsonPath` has already selected
16014
- the line). Defaults to `$`.
16017
+ line item (iteration over the sublist
16018
+ has already selected the line).
16019
+ Defaults to `$`.
16015
16020
  mapping:
16016
16021
  type: object
16017
16022
  description: |
@@ -16466,9 +16471,10 @@ components:
16466
16471
  SQL `SELECT` statement that resolves the lookup value.
16467
16472
  Typically returns a single row with a single column; use
16468
16473
  `extract` to pick the column when the query returns multiple.
16469
- Inject values from the incoming record with Handlebars — RDBMS
16470
- queries use triple braces and the `record.` prefix:
16471
- `SELECT id FROM users WHERE email = '{{{record.email}}}'`.
16474
+ Inject values from the incoming record with Handlebars and the
16475
+ `record.` prefix — double braces for strings, which the platform
16476
+ quotes and escapes, and triple braces for numbers:
16477
+ `SELECT id FROM users WHERE email = {{record.email}}`.
16472
16478
  extract:
16473
16479
  type: string
16474
16480
  description: |
@@ -16516,7 +16522,7 @@ components:
16516
16522
  description: |-
16517
16523
  SQL statements to execute; required when queryType is `["per_record"]`, `["per_page"]`, or `["first_page"]`, and typically contains a single statement.
16518
16524
  Each element is a complete SQL statement as a plain string — never a single bare string and never an array of objects (objects cause a Cast error).
16519
- Inject values from the incoming record with Handlebars using the `record.` prefix and triple braces, e.g. `'{{{record.name}}}'` for strings (single-quoted in the SQL) and `{{{record.quantity}}}` for numbers.
16525
+ Inject values from the incoming record with Handlebars using the `record.` prefix: double braces for strings and dates (`{{record.name}}`; the platform quotes and escapes the value) and triple braces for numbers (`{{{record.quantity}}}`).
16520
16526
  Rendering is strict: a reference the render model cannot resolve (a bare `{{name}}` without prefix, or `record.*` when the arriving record is a grouped array) fails the step with `cannot_evaluate_handlebars` — it does not render an empty value.
16521
16527
  `record` and `rows` are aliases chosen by the arriving data's shape: an object record binds `record` (`rows` undefined); a grouped array-record (e.g. upstream `groupByFields`) binds `rows` (`record` undefined) — address grouped rows as `{{rows.0.field}}` or iterate `{{#each rows}}`.
16522
16528
  Double braces (`{{record.field}}`) output the value wrapped in single quotes with embedded quotes doubled, while triple braces output it raw; block helpers such as `{{#each}}` and `{{#if}}` use double braces as normal.
@@ -16530,7 +16536,7 @@ components:
16530
16536
  - CORRECT: ["INSERT INTO users (name, email) VALUES ({{record.name}}, {{record.email}})"]
16531
16537
  - WRONG: "INSERT INTO users ..." (not an array)
16532
16538
  - WRONG: [{"query": "INSERT INTO users ..."}] (objects are invalid — causes Cast error)
16533
- - WRONG: ["INSERT INTO users (name) VALUES ('{{name}}')"] (missing record. prefix — rendering is strict, so the step fails with cannot_evaluate_handlebars)
16539
+ - WRONG: ["INSERT INTO users (name) VALUES ({{name}})"] (missing record. prefix — rendering is strict, so the step fails with cannot_evaluate_handlebars)
16534
16540
  - |-
16535
16541
  ## RDBMS HANDLEBARS SYNTAX
16536
16542
 
@@ -16564,7 +16570,7 @@ components:
16564
16570
  | Numeric column value (INT, DECIMAL, ...) | `{{{record.field}}}` |
16565
16571
  | Stored procedure raw arg (already typed) | `{{{record.field}}}` |
16566
16572
  | Identifier (table/column name) | `{{{record.field}}}` |
16567
- | JSON literal (`PARSE_JSON`/JSONB) | `{{{jsonSerialize record.subobject}}}` |
16573
+ | JSON literal (`PARSE_JSON`/JSONB) | `{{jsonSerialize record.subobject}}` (renders the JSON as a quoted string) |
16568
16574
 
16569
16575
  - **Block helpers** (`{{#each}}`, `{{#if}}`, `{{/each}}`,
16570
16576
  `{{#unless}}`) use double braces as normal — they emit no value.
@@ -16583,7 +16589,7 @@ components:
16583
16589
  - Per-page batch (different Handlebars context — see `queryType: per_page` below): `["{{#each batch_of_records}}UPDATE staging SET done=1 WHERE id={{{this.record.id}}};{{/each}}"]`
16584
16590
  examples:
16585
16591
  - - INSERT INTO orders (id, total) VALUES ({{{record.id}}}, {{{record.total}}})
16586
- - - '{{#each batch_of_records}}UPDATE customers SET name=''{{{record.name}}}'' WHERE id={{{record.id}}};{{/each}}'
16592
+ - - '{{#each batch_of_records}}UPDATE customers SET name={{record.name}} WHERE id={{{record.id}}};{{/each}}'
16587
16593
  queryType:
16588
16594
  type:
16589
16595
  - array
@@ -16612,7 +16618,7 @@ components:
16612
16618
  Prefer the highest-performance type the operation supports: bulk_load (Snowflake, Google BigQuery, Microsoft SQL Server on a cloud connection, Azure Synapse and Amazon Redshift — handles INSERT, upsert via `bulkLoad.primaryKeys`, and custom merge or ignore-existing logic via `bulkLoad.overrideMergeQuery`), then bulk_insert for pure INSERTs, then per_page, then per_record only when the logic cannot be expressed as a batch operation.
16613
16619
  For UPDATE, UPSERT, or ignore-existing logic on databases without bulk_load support, use per_page or per_record with `query` — bulk_insert has no duplicate-checking logic.
16614
16620
  Set a single value; do not combine types or use the legacy INSERT and UPDATE values.
16615
- per_page statements render against the whole batch (`batch_of_records`) rather than a single record — loop with `{{#each batch_of_records}}...{{{record.fieldName}}}...{{/each}}`.
16621
+ per_page statements render against the whole batch (`batch_of_records`) rather than a single record — loop with `{{#each batch_of_records}}...{{record.fieldName}}...{{/each}}`, with the same quoting as `query` (double braces for strings, triple for numbers).
16616
16622
 
16617
16623
  `bulk_load` — Stages the records as files and loads them with the database's native COPY / bulk mechanism for maximum throughput; supports INSERT, upsert via bulkLoad.primaryKeys, and custom merge or ignore-existing logic via bulkLoad.overrideMergeQuery; available on Snowflake, Google BigQuery, Microsoft SQL Server (cloud connections), Azure Synapse and Amazon Redshift (NetSuite Analytics Warehouse has it through the JDBC import). The import form calls it "Use optimized bulk load (recommended for larger imports)" and, beside it, calls bulk_insert "Use batch insert SQL query".
16618
16624
  `bulk_insert` — Batch INSERT via multi-row VALUES clause for efficient bulk data loading; has no upsert or ignore-existing logic and is available for all RDBMS types.
@@ -16659,7 +16665,7 @@ components:
16659
16665
  Prompt: "Import customers, ignore existing based on email"
16660
16666
  ```json
16661
16667
  "queryType": ["per_record"],
16662
- "query": ["INSERT INTO customers (name, email) VALUES ('{{{record.name}}}', '{{{record.email}}}')"]
16668
+ "query": ["INSERT INTO customers (name, email) VALUES ({{record.name}}, {{record.email}})"]
16663
16669
  ```
16664
16670
 
16665
16671
  ### Pure INSERT (no duplicate checking)
@@ -16673,7 +16679,7 @@ components:
16673
16679
  Prompt: "Update inventory counts"
16674
16680
  ```json
16675
16681
  "queryType": ["per_record"],
16676
- "query": ["UPDATE inventory SET qty = {{{record.qty}}} WHERE sku = '{{{record.sku}}}'"]
16682
+ "query": ["UPDATE inventory SET qty = {{{record.qty}}} WHERE sku = {{record.sku}}"]
16677
16683
  ```
16678
16684
 
16679
16685
  ### Run Once Per Page
@@ -16681,7 +16687,7 @@ components:
16681
16687
  ```json
16682
16688
  "queryType": ["per_page"]
16683
16689
  ```
16684
- **per_page uses a different Handlebars context than per_record.** The context is the entire batch (`batch_of_records`), not a single `record.` object. Loop through with `{{#each batch_of_records}}...{{{record.fieldName}}}...{{/each}}`.
16690
+ **per_page uses a different Handlebars context than per_record.** The context is the entire batch (`batch_of_records`), not a single `record.` object. Loop through with `{{#each batch_of_records}}...{{record.fieldName}}...{{/each}}`, quoting as in per_record: double braces for strings, triple braces for numbers.
16685
16691
 
16686
16692
  ### Run Once at Start
16687
16693
  Prompt: "Run this cleanup script before processing"
@@ -19641,8 +19647,8 @@ components:
19641
19647
  description: |-
19642
19648
  Configuration for a `ToolImport`, which executes a reusable Celigo Tool resource as the
19643
19649
  import action. The referenced tool defines its own input contract, processing pipeline, and
19644
- output; this object binds the tool to the import and optionally overrides the connections the
19645
- tool uses for this invocation.
19650
+ output; this object binds the tool to the import, along with the connections the tool's steps
19651
+ use for this invocation.
19646
19652
  x-celigo-ai-guidance:
19647
19653
  - |-
19648
19654
  A ToolImport is the **wrapper** that lets a flow / API / another Tool consume a
@@ -19693,7 +19699,8 @@ components:
19693
19699
  Per-import overrides applied to the referenced tool at execution time. Tools use a
19694
19700
  bring-your-own-keys connection model: the tool declares abstract connection slots, and the
19695
19701
  consumer binds concrete connections here so one tool definition can run against different
19696
- systems without being modified. Omit to run the tool with its default connections.
19702
+ systems without being modified. Bind every connection the tool's steps use; to keep the
19703
+ connection a step was authored against, bind it to itself.
19697
19704
  x-celigo-ai-guidance:
19698
19705
  - |-
19699
19706
  Per-consumer overrides applied when the wrapper invokes the underlying Tool.
@@ -19701,10 +19708,10 @@ components:
19701
19708
  imports each reference a `_connectionId`,
19702
19709
  and consumers bind real account connections to those abstract requirements here.
19703
19710
  - |-
19704
- Omit this block (or omit `connections` inside it) when the wrapper inherits the
19705
- Tool's authored connections as-is.
19706
- That's the right default when the Tool was authored against connections the
19707
- consumer also wants to use.
19711
+ The save accepts a wrapper without bindings, but when the Tool's steps use connections
19712
+ its run then fails with `connection_not_overridden`. Write one `connections` entry per
19713
+ connection the Tool's steps use, even when the wrapper keeps the Tool's authored
19714
+ connection (`_id` equal to `_abstractId`); a Tool whose steps use no connection needs none.
19708
19715
  properties:
19709
19716
  connections:
19710
19717
  type: array
@@ -20772,7 +20779,6 @@ components:
20772
20779
  - gpt-5-mini
20773
20780
  - gpt-5-nano
20774
20781
  - gpt-4.1-mini
20775
- - gpt-4o
20776
20782
  x-celigo-ui-override: Required by the AI agent configuration form. Encoded here to mirror the form so builders produce connectable configurations.
20777
20783
  x-celigo-ai-guidance:
20778
20784
  - |-
@@ -25057,15 +25063,16 @@ components:
25057
25063
  - filter
25058
25064
  description: |-
25059
25065
  Filter applied to incoming records before they are sent to the destination system.
25060
- Records that match continue through the import; records that don't are silently
25061
- dropped. Filter expressions reference the incoming record's fields.
25066
+ Records that match continue through the import; records that don't skip this import,
25067
+ counted as ignored, and continue to the next step. Filter expressions reference the
25068
+ incoming record's fields.
25062
25069
  allOf:
25063
25070
  - $ref: '#/components/schemas/Filter'
25064
25071
  x-celigo-ai-guidance:
25065
25072
  - |-
25066
25073
  ## IMPORT-SPECIFIC BEHAVIOR
25067
25074
 
25068
- **Pre-Import Filtering**: Filters are applied to incoming records before they are sent to the destination system. Records that don't match the filter criteria are silently dropped and not imported.
25075
+ **Pre-Import Filtering**: Filters are applied to incoming records before they are sent to the destination system. A record that doesn't match is not sent, but it is not removed from the flow: it continues to the next step without this import's response-mapped fields, so a later step that writes those fields needs its own filter.
25069
25076
  traceKeyTemplate:
25070
25077
  type: string
25071
25078
  x-celigo-agent:
@@ -25182,7 +25189,7 @@ components:
25182
25189
  mapping step, on the raw upstream records. Engage when the
25183
25190
  user needs to reshape, validate, filter, or annotate records
25184
25191
  before the mapping sees them (drop archived records,
25185
- normalize date strings, split a record into many). Don't
25192
+ normalize date strings). Don't
25186
25193
  engage for static value substitution or simple field
25187
25194
  renames — mappings handle those.
25188
25195
 
@@ -25458,7 +25465,7 @@ components:
25458
25465
  resultField, ...)
25459
25466
  - ``netsuite_da.lookups`` — NetSuite search (recordType,
25460
25467
  searchField, operator, ...)
25461
- - ``http.lookups`` — HTTP request (method, relativeURI, postBody,
25468
+ - ``http.lookups`` — HTTP request (method, relativeURI, body,
25462
25469
  extract, ...)
25463
25470
  - ``jdbc.lookups`` / ``rdbms.lookups`` — SQL query (query,
25464
25471
  extract, ...)
@@ -29806,7 +29813,7 @@ components:
29806
29813
  Selects which files are processed: files matching the filter criteria are exported,
29807
29814
  and non-matching files are skipped entirely before processing begins. Filterable
29808
29815
  fields come from each file's metadata — most providers expose filename, filesize,
29809
- and lastmodified (e.g. ["endswith", ["extract", "filename"], ".csv"]).
29816
+ and lastmodified (e.g. ["endswith", ["string", ["extract", "filename"]], ".csv"]).
29810
29817
  allOf:
29811
29818
  - $ref: '#/components/schemas/Filter'
29812
29819
  x-celigo-ai-guidance:
@@ -29821,7 +29828,7 @@ components:
29821
29828
 
29822
29829
  1. **filename**: The name of the file (with extension)
29823
29830
  - Example filter: Match files with specific extensions or naming patterns
29824
- - Usage: `["endswith", ["extract", "filename"], ".csv"]`
29831
+ - Usage: `["endswith", ["string", ["extract", "filename"]], ".csv"]`
29825
29832
 
29826
29833
  2. **filesize**: The size of the file in bytes
29827
29834
  - Example filter: Skip files that are too large or too small
@@ -29829,7 +29836,7 @@ components:
29829
29836
 
29830
29837
  3. **lastmodified**: The last modification timestamp of the file
29831
29838
  - Example filter: Process only files created/modified within a specific date range
29832
- - Usage: `["greaterthan", ["extract", "lastmodified"], "2023-01-01T00:00:00Z"]`
29839
+ - Usage: `["greaterthan", ["epochtime", ["extract", "lastmodified"]], 1672531200000]` (2023-01-01T00:00:00Z in epoch milliseconds)
29833
29840
  backupPath:
29834
29841
  type: string
29835
29842
  description: Path where backup copies of source files are stored before processing.
@@ -30090,38 +30097,10 @@ components:
30090
30097
  bearerToken:
30091
30098
  type: string
30092
30099
  description: |-
30093
- Bearer token used to authenticate inbound distributed export requests. Optional — most
30094
- distributed listeners (NetSuite, Salesforce real-time) use no token. Write-only and stored
30095
- encrypted; treat it as a sensitive credential.
30096
- x-celigo-ai-guidance:
30097
- - |-
30098
- ## FIELD BEHAVIOR
30099
-
30100
- This token provides authentication for the distributed export:
30101
-
30102
- - Required for secure access to distributed endpoints
30103
- - Must be a valid bearer token format
30104
- - Used in Authorization header as "Bearer {token}"
30105
- - Should be kept secure and rotated regularly
30106
- - |-
30107
- ## IMPLEMENTATION GUIDANCE
30108
-
30109
- ### Token management
30110
-
30111
- - Store tokens securely (encrypted at rest)
30112
- - Implement token rotation policies
30113
- - Monitor token expiration dates
30114
- - Use environment variables for token storage
30115
-
30116
- ### Security considerations
30117
-
30118
- - Never log bearer tokens in plain text
30119
- - Implement proper access controls
30120
- - Use HTTPS for all token transmissions
30121
- - Validate tokens on each request
30122
-
30123
- IMPORTANT: Bearer tokens provide full access to the distributed export.
30124
- Treat them as sensitive credentials.
30100
+ Bearer token that a system pushing data into this export sends as
30101
+ `Authorization: Bearer <token>`. Optional — most distributed listeners (NetSuite,
30102
+ Salesforce real-time) use no token. Write-only and stored encrypted; treat it as a
30103
+ sensitive credential.
30125
30104
  format: password
30126
30105
  examples:
30127
30106
  - eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
@@ -30517,7 +30496,7 @@ components:
30517
30496
  (`http.paging.body` likewise from its paging fields). A body that is not parseable JSON at
30518
30497
  rest — Handlebars block helpers outside a string value — renders every field EMPTY in that
30519
30498
  form, so keep templated variables inside a JSON string value:
30520
- `"variables": "{\"after\": {{#if export.http.paging.token}}\"{{export.http.paging.token}}\"{{else}}null{{/if}}}"`.
30499
+ `"variables": "{\"after\": {{#if export.http.paging.token}}\"{{export.http.paging.token}}\"{{else}}null{{/if}} }"`.
30521
30500
  - |-
30522
30501
  The GraphQL form has no relative URI field: the connection `baseURI` is expected to be the
30523
30502
  GraphQL endpoint (a saved `relativeURI` still applies but is not shown). Create GraphQL
@@ -36694,9 +36673,10 @@ components:
36694
36673
 
36695
36674
  In a flow test run, and for an upstream lookup during a page-processor preview
36696
36675
  (`previewPageProcessor`), integrator.io substitutes this mock output for the export's live
36697
- response instead of calling the source. It is not substituted anywhere else: a target lookup
36698
- in that preview runs its live query, `POST /v1/exports/preview` calls the source, and real
36699
- runs ignore it. This allows you to:
36676
+ response instead of calling the source. A saved mock is not read anywhere else: a target
36677
+ lookup in that preview runs its live query, invoking the export
36678
+ (`POST /v1/exports/{_id}/invoke`) reads the source, and real runs ignore it. This allows you
36679
+ to:
36700
36680
  - Test mappings with representative data
36701
36681
  - Configure downstream flow steps without waiting for real data
36702
36682
  - Simulate various data scenarios
@@ -36705,6 +36685,11 @@ components:
36705
36685
  `transform` and output `filter` still run on top of them, so downstream steps — and a
36706
36686
  lookup's results mapping — see the transformed, filtered records, not the shape stored here.
36707
36687
 
36688
+ `POST /v1/exports/preview` reads the `mockOutput` in its request body, not the saved one. For
36689
+ an export the preview supports (an HTTP export, for example), a body that carries it (as an
36690
+ export read back with GET does) returns that snapshot as a lone `parse` stage without calling
36691
+ the source; remove `mockOutput` from the body to preview live records.
36692
+
36708
36693
  **Limitations**
36709
36694
 
36710
36695
  - Maximum of 10 records
@@ -36722,8 +36707,8 @@ components:
36722
36707
  the stored shape walks paths that do not exist at runtime.
36723
36708
  - |-
36724
36709
  A saved mockOutput is read in exactly two places — a flow test run, and an upstream lookup
36725
- during a page-processor preview. A target lookup's preview, `POST /v1/exports/preview` and
36726
- every real run call the source and never read it.
36710
+ during a page-processor preview. A target lookup's preview, `POST /v1/exports/{_id}/invoke`
36711
+ and every real run call the source and never read it.
36727
36712
  properties:
36728
36713
  page_of_records:
36729
36714
  type: array
@@ -37165,9 +37150,9 @@ components:
37165
37150
  x-celigo-agent:
37166
37151
  - filter
37167
37152
  description: |-
37168
- Filter applied to records immediately after they are retrieved from the source.
37169
- Records that match continue through the flow; records that don't are silently dropped.
37170
- Filter expressions reference the exported record's own fields.
37153
+ Filter applied to each record after the export's transform and before its preSavePage
37154
+ hook. Records that match continue through the flow; records that don't are silently
37155
+ dropped. Filter expressions reference the transformed record's fields.
37171
37156
  allOf:
37172
37157
  - $ref: '#/components/schemas/Filter'
37173
37158
  x-celigo-ai-guidance:
@@ -37177,13 +37162,13 @@ components:
37177
37162
 
37178
37163
  ## FILTER BEHAVIOR
37179
37164
 
37180
- When configured, the filter is applied immediately after records are retrieved from the source system:
37165
+ When configured, the filter is applied to each record after the export's transform and before the preSavePage hook:
37181
37166
  - Records that match the filter criteria continue through the flow
37182
37167
  - Records that don't match are silently dropped
37183
37168
  - No partial record processing is performed
37184
37169
 
37185
37170
  ## AVAILABLE FILTER FIELDS
37186
- The fields available for filtering are the data fields from each record retrieved by the export.
37171
+ The fields available for filtering are those of each record as the export's transform leaves it (the retrieved record when no transform is set).
37187
37172
  inputFilter:
37188
37173
  x-celigo-agent:
37189
37174
  - filter
@@ -37296,9 +37281,12 @@ components:
37296
37281
  x-celigo-agent:
37297
37282
  - script
37298
37283
  description: |-
37299
- Hook that runs after each page of records is retrieved from the source but before it
37300
- is sent to downstream steps. Commonly used to flatten structures, drop unwanted
37301
- records, or add computed fields.
37284
+ Hook that runs on each page of records after the export's transform and output
37285
+ filter, before the page is sent to downstream steps; commonly used to flatten
37286
+ structures, drop unwanted records, or add computed fields. It receives the page as
37287
+ `options.data` (with `errors`, `pageIndex`, `configuration` and `settings`) and returns
37288
+ `{data, errors, abort, newErrorsAndRetryData}`. An exception thrown here fails every
37289
+ record on that page, each with its own retry data, and later pages still run.
37302
37290
  properties:
37303
37291
  function:
37304
37292
  type: string
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celigo/api-specs",
3
- "version": "0.2.132",
3
+ "version": "0.2.134",
4
4
  "private": false,
5
5
  "description": "Public OpenAPI bundles (dist/) and the celigo-cli lint schema pack (schemas.json).",
6
6
  "keywords": [
package/schemas.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "provenance": {
3
3
  "repo": "celigo/integrator-api-specs",
4
- "iasCommit": "9f942658",
4
+ "iasCommit": "86f54b3d",
5
5
  "generatedAt": "2026-10-21"
6
6
  },
7
7
  "roots": {
@@ -6674,13 +6674,13 @@
6674
6674
  "p": {
6675
6675
  "_lookupCacheId": 1,
6676
6676
  "allowFailures": 118,
6677
+ "body": 1,
6677
6678
  "default": 88,
6678
6679
  "extract": 1,
6679
6680
  "headers": 260,
6680
6681
  "map": 117,
6681
6682
  "method": 164,
6682
6683
  "name": 1,
6683
- "postBody": 1,
6684
6684
  "relativeURI": 1,
6685
6685
  "useDefaultOnMultipleMatches": 9
6686
6686
  }