@celigo/api-specs 0.2.112 → 0.2.114

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/api.yml CHANGED
@@ -6470,6 +6470,7 @@ components:
6470
6470
  but a decimal is rejected with 422 `invalid_field_value` on every write (the same rule applies to every
6471
6471
  adaptor's `concurrencyLevel` and `targetConcurrencyLevel`, and to `ftp.multiThreadCount`).
6472
6472
  Platform-managed while `autoRecoverRateLimitErrors` is true — a PUT ignores the value and the recovery machinery moves it toward the target; set `targetConcurrencyLevel` instead.
6473
+ At level 1 an import's pages run one at a time, so a record's existence lookup and write finish before the next record's start (measured on a single import); the import's `idLockTemplate` serializes only same-key records and leaves the rest parallel.
6473
6474
  minimum: 1
6474
6475
  maximum: 50
6475
6476
  default: 25
@@ -5581,6 +5581,7 @@ components:
5581
5581
  but a decimal is rejected with 422 `invalid_field_value` on every write (the same rule applies to every
5582
5582
  adaptor's `concurrencyLevel` and `targetConcurrencyLevel`, and to `ftp.multiThreadCount`).
5583
5583
  Platform-managed while `autoRecoverRateLimitErrors` is true — a PUT ignores the value and the recovery machinery moves it toward the target; set `targetConcurrencyLevel` instead.
5584
+ At level 1 an import's pages run one at a time, so a record's existence lookup and write finish before the next record's start (measured on a single import); the import's `idLockTemplate` serializes only same-key records and leaves the rest parallel.
5584
5585
  minimum: 1
5585
5586
  maximum: 50
5586
5587
  default: 25
package/dist/enduser.yml CHANGED
@@ -3682,6 +3682,7 @@ components:
3682
3682
  but a decimal is rejected with 422 `invalid_field_value` on every write (the same rule applies to every
3683
3683
  adaptor's `concurrencyLevel` and `targetConcurrencyLevel`, and to `ftp.multiThreadCount`).
3684
3684
  Platform-managed while `autoRecoverRateLimitErrors` is true — a PUT ignores the value and the recovery machinery moves it toward the target; set `targetConcurrencyLevel` instead.
3685
+ At level 1 an import's pages run one at a time, so a record's existence lookup and write finish before the next record's start (measured on a single import); the import's `idLockTemplate` serializes only same-key records and leaves the rest parallel.
3685
3686
  minimum: 1
3686
3687
  maximum: 50
3687
3688
  default: 25
package/dist/export.yml CHANGED
@@ -2074,6 +2074,7 @@ components:
2074
2074
  x-celigo-ai-guidance:
2075
2075
  - 'Set this only when the records sit inside an object (`{"data": {"items": [...]}}` → `data.items`). A sender that already posts a top-level array needs nothing here; a sender that posts an object with no path configured delivers exactly one record per request — the whole object — which is also the way to make one request one record on purpose.'
2076
2076
  - 'Records are paged per request at the export''s `pageSize` (default 20) before any hook, filter, or downstream step runs: a 25-element array is a page of 20 records followed by a page of 5, and a `preSavePage` hook receives one page (up to `pageSize` records) per call. A hook that groups rows by order therefore groups only the rows on the same page — an order whose rows span two pages comes out as two grouped records, the second after the first was already processed. To keep a request''s rows together, have the sender wrap them in an object (one record, rows under `record.<path>`) or raise `pageSize` above the largest request — never `groupByFields`, which the listener applies per page after paging.'
2077
+ - 'Requests that arrive together, or while the listener''s current run is still collecting, become additional pages of that same run; a request that arrives after the run has stopped collecting starts a new run. Nothing on the listener de-duplicates: a sender that fires the same event twice (a retry, a button clicked twice) delivers two records, and the step that writes the effect has to be made idempotent — `ignoreExisting` or a keyed upsert under the import''s `idLockTemplate`.'
2077
2078
  examples:
2078
2079
  - events
2079
2080
  - data.items
@@ -16062,7 +16063,9 @@ components:
16062
16063
  `{{join "_" record.customerId record.orderId}}`). When omitted, the system picks a unique
16063
16064
  field automatically. If a transform reshapes the data first, omit the `record.` prefix.
16064
16065
  Trace keys are capped at 256 characters; a longer key is stored truncated from the middle,
16065
- keeping the beginning and end of the value.
16066
+ keeping the beginning and end of the value. Downstream imports use the trace key as their
16067
+ default concurrency lock key (a blank key means no lock); it never de-duplicates — a record
16068
+ emitted twice with the same key is processed twice.
16066
16069
  examples:
16067
16070
  - '{{record.id}}'
16068
16071
  - '{{join "_" record.customerId record.orderId}}'
package/dist/flow.yml CHANGED
@@ -835,8 +835,10 @@ paths:
835
835
  To trace a run end-to-end, arm debug logging before calling this, then use the returned
836
836
  `_jobId` with the execution-log endpoints. For delta backfills, prefer passing explicit
837
837
  `startDate`/`endDate` over editing the export's `lastExportDateTime` — the override is
838
- scoped to one run. A 200 with `_jobId` absent means the request was accepted but no job
839
- was queued, usually because another run is already in progress.
838
+ scoped to one run. A flow runs one instance at a time: a run requested while another
839
+ instance is queued or running is refused with `409 job_already_queued` ("Another instance
840
+ for this flow is already queued or running"), and a run-next-flow trigger that arrives in
841
+ that state is dropped rather than queued.
840
842
  parameters:
841
843
  - name: _id
842
844
  in: path
@@ -890,6 +892,20 @@ paths:
890
892
  $ref: '#/components/responses/401-unauthorized'
891
893
  '404':
892
894
  $ref: '#/components/responses/404-not-found'
895
+ '409':
896
+ description: |-
897
+ Another instance of this flow is already queued or running. Wait for it to
898
+ finish (poll `GET /v1/flows/{_id}/jobs/latest`) and request the run again.
899
+ content:
900
+ application/json:
901
+ schema:
902
+ $ref: '#/components/schemas/Error'
903
+ examples:
904
+ default:
905
+ value:
906
+ errors:
907
+ - code: job_already_queued
908
+ message: Another instance for this flow is already queued or running. Please try again later.
893
909
  '422':
894
910
  $ref: '#/components/responses/422-unprocessable-entity'
895
911
  /v1/flows/{_flowId}/runs/stats:
@@ -5531,7 +5547,9 @@ components:
5531
5547
  description: |-
5532
5548
  Flow IDs to trigger after this flow completes successfully. Accepts null as
5533
5549
  equivalent to an empty list (the server also serves null on documents saved
5534
- that way).
5550
+ that way). The trigger starts the next flow from its own source steps and passes
5551
+ it no records; a trigger that fires while the next flow is already queued or running
5552
+ is dropped, not queued, so several completions in quick succession start it once.
5535
5553
  items:
5536
5554
  type: string
5537
5555
  format: objectId
package/dist/import.yml CHANGED
@@ -3802,23 +3802,25 @@ components:
3802
3802
  - |-
3803
3803
  Two ways to specify the lookup, picked per import:
3804
3804
 
3805
+ - **Expression form** — the default choice: set `expression` to a
3806
+ NetSuite search-expression literal (the canonical patterns are on
3807
+ the `expression` field — `["internalid","is","{{{id}}}"]` for a
3808
+ record that carries the internal id, `externalid` and `cust*_*`
3809
+ variants, `anyof` for multi-match, AND/OR compounds). When
3810
+ `expression` is set, `searchField` / `operator` / `extract` are
3811
+ ignored.
3805
3812
  - **Simple form** — set `searchField` + `operator` (+ optional
3806
- `extract`). Use for single-criterion lookups against a built-in
3807
- NetSuite field. The platform builds the equivalent search
3808
- expression for you at runtime.
3809
- - **Expression form** — set `expression` to a NetSuite search-
3810
- expression literal. Use for compound (AND/OR) lookups, custom-
3811
- record-field lookups (`custbody_*` / `custrecord_*` /
3812
- `custentity_*`), and the `anyof` operator that the simple form
3813
- doesn't cover. When `expression` is set, `searchField` /
3814
- `operator` / `extract` are ignored. The expression form is the
3815
- common pattern in practice.
3813
+ `extract`) for a single-criterion lookup against a built-in
3814
+ NetSuite field; the platform builds the equivalent search
3815
+ expression at runtime. For the internal id itself the expression
3816
+ form above is the preferred path, and `anyof` is available only
3817
+ through `expression`.
3816
3818
  - |-
3817
3819
  The lookup result is what identifies the target record; on `update` a source
3818
3820
  field mapped to `internalid` is overridden by it. When the incoming record
3819
- already carries the NetSuite internal ID, point the lookup at that field
3820
- (simple form with `extract`, or an expression on the id field) rather than
3821
- removing the lookup.
3821
+ already carries the NetSuite internal ID, point the lookup at that field with
3822
+ the expression form — `["internalid","is","{{{id}}}"]` — rather than removing
3823
+ the lookup.
3822
3824
  properties:
3823
3825
  extract:
3824
3826
  type: string
@@ -3827,7 +3829,7 @@ components:
3827
3829
  - |-
3828
3830
  Path on the incoming source record (NOT the post-mapped record) whose value drives the lookup.
3829
3831
  Used with the simple form (`searchField` + `operator`).
3830
- Common values: `"internalId"`, `"externalId"`, `"id"`.
3832
+ Common values: `"externalId"`, `"id"`.
3831
3833
  Ignored when `expression` is set.
3832
3834
  examples:
3833
3835
  - externalId
@@ -3837,8 +3839,9 @@ components:
3837
3839
  x-celigo-ai-guidance:
3838
3840
  - |-
3839
3841
  NetSuite field the simple-form lookup queries against.
3840
- Common values: `"externalId"`, `"internalId"`, `"email"`, `"tranId"`,
3841
- `"name"`.
3842
+ Common values: `"externalId"`, `"email"`, `"tranId"`, `"name"`.
3843
+ Matching on the record's internal id is the expression form —
3844
+ `["internalid","is","{{{id}}}"]` — not a `searchField`.
3842
3845
  Ignored when `expression` is set.
3843
3846
  examples:
3844
3847
  - externalId
@@ -3848,9 +3851,9 @@ components:
3848
3851
  x-celigo-ai-guidance:
3849
3852
  - |-
3850
3853
  Comparison operator for the simple-form lookup.
3851
- Common values: `"is"` (exact match),
3852
- `"anyof"` (matches any of the given internal-id values), `"contains"`,
3853
- `"startswith"`, `"equalto"`.
3854
+ Common values: `"is"` (exact match), `"contains"`, `"startswith"`,
3855
+ `"equalto"`. Multi-match (`anyof`) is available only through
3856
+ `expression` (`["internalid","anyof","{{{id}}}"]`).
3854
3857
  Ignored when `expression` is set.
3855
3858
  expression:
3856
3859
  type: string
@@ -5636,12 +5639,20 @@ components:
5636
5639
  type:
5637
5640
  - string
5638
5641
  - 'null'
5639
- description: Name of the lookup used to determine which records to ignore when ignore-existing is enabled.
5642
+ description: |-
5643
+ Name of an entry in `lookups` that probes the database per record to decide whether it
5644
+ already exists, so `ignoreExisting` can skip it (or `ignoreMissing` can skip records with
5645
+ no match). Use it when existence must be read from the database; use `ignoreExtract` when
5646
+ the incoming record already carries the field that signals existence.
5640
5647
  ignoreExtract:
5641
5648
  type:
5642
5649
  - string
5643
5650
  - 'null'
5644
- description: Path used to extract the value that determines whether a record is ignored when ignore-existing is enabled.
5651
+ description: |-
5652
+ Path to a field on the incoming record whose presence decides the skip: with
5653
+ `ignoreExisting`, a populated value marks the record as existing and it is skipped; with
5654
+ `ignoreMissing`, an empty value marks it as missing and it is skipped. Zero per-record
5655
+ overhead; set exactly one of `ignoreExtract` / `ignoreLookupName`.
5645
5656
  if:
5646
5657
  not:
5647
5658
  propertyNames:
@@ -15922,17 +15933,48 @@ components:
15922
15933
  type: boolean
15923
15934
  description: |-
15924
15935
  When true, records that do not already exist in the destination system are silently
15925
- skipped instead of producing errors — used for update-only operations.
15936
+ skipped instead of producing errors — used for update-only operations. Existing records
15937
+ are identified the same way as for `ignoreExisting`: the adaptor's ignore lookup
15938
+ (`ignoreLookupName`) or a populated `ignoreExtract` field on the incoming record.
15926
15939
  idLockTemplate:
15927
15940
  type:
15928
15941
  - string
15929
15942
  - 'null'
15930
15943
  description: |-
15931
- Handlebars template that generates a lock key for each record so records resolving to
15932
- the same key are not submitted concurrently, preventing duplicate or conflicting writes
15933
- to the same target record.
15944
+ Handlebars template that generates a concurrency lock key for each record: records that
15945
+ render the same key are never submitted at the same time, while records with different
15946
+ keys are processed in parallel. When omitted, the record's trace key is the lock key (a
15947
+ blank trace key means no lock); a template replaces that default rather than adding to it.
15948
+ The template is rendered from the record as it arrives at the import — before the import's
15949
+ own lookups and mapping run — so a key built from one of the import's lookup results
15950
+ (`{{lookup.<name>}}`) or from a mapped field renders empty, and an empty key is no lock at
15951
+ all; a field added upstream by a lookup step's response mapping is on the record and works.
15952
+ x-celigo-ai-guidance:
15953
+ - |-
15954
+ The lock is scoped to this import resource across every page and every concurrent run
15955
+ that submits through it — including other flows sharing the import — not to one page,
15956
+ and not to the connection: two different imports rendering the same key on the same
15957
+ connection still run in parallel. Records within a single page are
15958
+ sent one at a time regardless, so the lock matters when same-key records sit in different
15959
+ pages or runs.
15960
+ - |-
15961
+ With `ignoreExisting` and an ignore lookup, the existence lookup runs inside the lock:
15962
+ lookup-then-write for one key does not interleave with another record's lookup-then-write
15963
+ for the same key. That pairing is the duplicate-prevention pattern for destinations that
15964
+ can be queried but do not reject duplicates — key the lock on the business identifier the
15965
+ write is keyed on. With the check but no lock, concurrent same-key pages all read a miss
15966
+ before any write lands and all create. Connection `concurrencyLevel: 1` also closes that
15967
+ race for the import (measured on a single import: its pages ran one at a time and the
15968
+ same-key pages created once) — the lock is the per-key tool that leaves unrelated records
15969
+ parallel.
15970
+ - |-
15971
+ Key it on a field the record carries when it reaches this import (`{{{record.externalId}}}-GR`,
15972
+ or a field an upstream lookup step added through its response mapping), never on one of
15973
+ this import's own lookup results; the key must render non-empty for the records that
15974
+ matter, or those records have no lock.
15934
15975
  examples:
15935
15976
  - '{{record.customerId}}'
15977
+ - '{{{record.externalId}}}-GR'
15936
15978
  dataURITemplate:
15937
15979
  type:
15938
15980
  - string
@@ -16052,7 +16094,9 @@ components:
16052
16094
  Handlebars template that overrides how each record's unique trace key is generated,
16053
16095
  used to track records through the flow and match errors to records. Trace keys are
16054
16096
  capped at 256 characters; a longer key is stored truncated from the middle, keeping
16055
- the beginning and end of the value.
16097
+ the beginning and end of the value. The trace key is also the import's default
16098
+ concurrency lock key when `idLockTemplate` is not set; it never de-duplicates — a record
16099
+ that arrives twice with the same key is processed twice.
16056
16100
  x-celigo-ai-guidance:
16057
16101
  - |-
16058
16102
  ## CRITICAL: ONLY VALID WHEN ``oneToMany: true``
@@ -410,7 +410,12 @@ paths:
410
410
  summary: Upsert entries into a lookup cache
411
411
  description: |-
412
412
  Writes key-value entries to a lookup cache. Keys that already exist are
413
- overwritten; new keys are created. Values may be null, numbers, booleans,
413
+ overwritten; new keys are created. There is no insert-only, create-if-absent or
414
+ compare-and-set mode — a write never fails because the key exists, so a
415
+ "first writer wins" claim cannot be built on this call alone: make the claim
416
+ on the flow's import step instead (`ignoreExisting` with a `getData` lookup on
417
+ the key, under `idLockTemplate` on the same key), which serializes
418
+ read-then-write per key. Values may be null, numbers, booleans,
414
419
  strings, or objects — arrays at the top level are rejected (wrap them in an
415
420
  object). One invalid entry fails the entire batch and nothing in it is written:
416
421
  a bad value returns `400 invalid_value`, and an empty, missing, or over-length
@@ -5507,6 +5507,7 @@ components:
5507
5507
  but a decimal is rejected with 422 `invalid_field_value` on every write (the same rule applies to every
5508
5508
  adaptor's `concurrencyLevel` and `targetConcurrencyLevel`, and to `ftp.multiThreadCount`).
5509
5509
  Platform-managed while `autoRecoverRateLimitErrors` is true — a PUT ignores the value and the recovery machinery moves it toward the target; set `targetConcurrencyLevel` instead.
5510
+ At level 1 an import's pages run one at a time, so a record's existence lookup and write finish before the next record's start (measured on a single import); the import's `idLockTemplate` serializes only same-key records and leaves the rest parallel.
5510
5511
  minimum: 1
5511
5512
  maximum: 50
5512
5513
  default: 25
package/dist/tool.yml CHANGED
@@ -8836,6 +8836,7 @@ components:
8836
8836
  but a decimal is rejected with 422 `invalid_field_value` on every write (the same rule applies to every
8837
8837
  adaptor's `concurrencyLevel` and `targetConcurrencyLevel`, and to `ftp.multiThreadCount`).
8838
8838
  Platform-managed while `autoRecoverRateLimitErrors` is true — a PUT ignores the value and the recovery machinery moves it toward the target; set `targetConcurrencyLevel` instead.
8839
+ At level 1 an import's pages run one at a time, so a record's existence lookup and write finish before the next record's start (measured on a single import); the import's `idLockTemplate` serializes only same-key records and leaves the rest parallel.
8839
8840
  minimum: 1
8840
8841
  maximum: 50
8841
8842
  default: 25
@@ -15381,23 +15382,25 @@ components:
15381
15382
  - |-
15382
15383
  Two ways to specify the lookup, picked per import:
15383
15384
 
15385
+ - **Expression form** — the default choice: set `expression` to a
15386
+ NetSuite search-expression literal (the canonical patterns are on
15387
+ the `expression` field — `["internalid","is","{{{id}}}"]` for a
15388
+ record that carries the internal id, `externalid` and `cust*_*`
15389
+ variants, `anyof` for multi-match, AND/OR compounds). When
15390
+ `expression` is set, `searchField` / `operator` / `extract` are
15391
+ ignored.
15384
15392
  - **Simple form** — set `searchField` + `operator` (+ optional
15385
- `extract`). Use for single-criterion lookups against a built-in
15386
- NetSuite field. The platform builds the equivalent search
15387
- expression for you at runtime.
15388
- - **Expression form** — set `expression` to a NetSuite search-
15389
- expression literal. Use for compound (AND/OR) lookups, custom-
15390
- record-field lookups (`custbody_*` / `custrecord_*` /
15391
- `custentity_*`), and the `anyof` operator that the simple form
15392
- doesn't cover. When `expression` is set, `searchField` /
15393
- `operator` / `extract` are ignored. The expression form is the
15394
- common pattern in practice.
15393
+ `extract`) for a single-criterion lookup against a built-in
15394
+ NetSuite field; the platform builds the equivalent search
15395
+ expression at runtime. For the internal id itself the expression
15396
+ form above is the preferred path, and `anyof` is available only
15397
+ through `expression`.
15395
15398
  - |-
15396
15399
  The lookup result is what identifies the target record; on `update` a source
15397
15400
  field mapped to `internalid` is overridden by it. When the incoming record
15398
- already carries the NetSuite internal ID, point the lookup at that field
15399
- (simple form with `extract`, or an expression on the id field) rather than
15400
- removing the lookup.
15401
+ already carries the NetSuite internal ID, point the lookup at that field with
15402
+ the expression form — `["internalid","is","{{{id}}}"]` — rather than removing
15403
+ the lookup.
15401
15404
  properties:
15402
15405
  extract:
15403
15406
  type: string
@@ -15406,7 +15409,7 @@ components:
15406
15409
  - |-
15407
15410
  Path on the incoming source record (NOT the post-mapped record) whose value drives the lookup.
15408
15411
  Used with the simple form (`searchField` + `operator`).
15409
- Common values: `"internalId"`, `"externalId"`, `"id"`.
15412
+ Common values: `"externalId"`, `"id"`.
15410
15413
  Ignored when `expression` is set.
15411
15414
  examples:
15412
15415
  - externalId
@@ -15416,8 +15419,9 @@ components:
15416
15419
  x-celigo-ai-guidance:
15417
15420
  - |-
15418
15421
  NetSuite field the simple-form lookup queries against.
15419
- Common values: `"externalId"`, `"internalId"`, `"email"`, `"tranId"`,
15420
- `"name"`.
15422
+ Common values: `"externalId"`, `"email"`, `"tranId"`, `"name"`.
15423
+ Matching on the record's internal id is the expression form —
15424
+ `["internalid","is","{{{id}}}"]` — not a `searchField`.
15421
15425
  Ignored when `expression` is set.
15422
15426
  examples:
15423
15427
  - externalId
@@ -15427,9 +15431,9 @@ components:
15427
15431
  x-celigo-ai-guidance:
15428
15432
  - |-
15429
15433
  Comparison operator for the simple-form lookup.
15430
- Common values: `"is"` (exact match),
15431
- `"anyof"` (matches any of the given internal-id values), `"contains"`,
15432
- `"startswith"`, `"equalto"`.
15434
+ Common values: `"is"` (exact match), `"contains"`, `"startswith"`,
15435
+ `"equalto"`. Multi-match (`anyof`) is available only through
15436
+ `expression` (`["internalid","anyof","{{{id}}}"]`).
15433
15437
  Ignored when `expression` is set.
15434
15438
  expression:
15435
15439
  type: string
@@ -16946,12 +16950,20 @@ components:
16946
16950
  type:
16947
16951
  - string
16948
16952
  - 'null'
16949
- description: Name of the lookup used to determine which records to ignore when ignore-existing is enabled.
16953
+ description: |-
16954
+ Name of an entry in `lookups` that probes the database per record to decide whether it
16955
+ already exists, so `ignoreExisting` can skip it (or `ignoreMissing` can skip records with
16956
+ no match). Use it when existence must be read from the database; use `ignoreExtract` when
16957
+ the incoming record already carries the field that signals existence.
16950
16958
  ignoreExtract:
16951
16959
  type:
16952
16960
  - string
16953
16961
  - 'null'
16954
- description: Path used to extract the value that determines whether a record is ignored when ignore-existing is enabled.
16962
+ description: |-
16963
+ Path to a field on the incoming record whose presence decides the skip: with
16964
+ `ignoreExisting`, a populated value marks the record as existing and it is skipped; with
16965
+ `ignoreMissing`, an empty value marks it as missing and it is skipped. Zero per-record
16966
+ overhead; set exactly one of `ignoreExtract` / `ignoreLookupName`.
16955
16967
  if:
16956
16968
  not:
16957
16969
  propertyNames:
@@ -24789,17 +24801,48 @@ components:
24789
24801
  type: boolean
24790
24802
  description: |-
24791
24803
  When true, records that do not already exist in the destination system are silently
24792
- skipped instead of producing errors — used for update-only operations.
24804
+ skipped instead of producing errors — used for update-only operations. Existing records
24805
+ are identified the same way as for `ignoreExisting`: the adaptor's ignore lookup
24806
+ (`ignoreLookupName`) or a populated `ignoreExtract` field on the incoming record.
24793
24807
  idLockTemplate:
24794
24808
  type:
24795
24809
  - string
24796
24810
  - 'null'
24797
24811
  description: |-
24798
- Handlebars template that generates a lock key for each record so records resolving to
24799
- the same key are not submitted concurrently, preventing duplicate or conflicting writes
24800
- to the same target record.
24812
+ Handlebars template that generates a concurrency lock key for each record: records that
24813
+ render the same key are never submitted at the same time, while records with different
24814
+ keys are processed in parallel. When omitted, the record's trace key is the lock key (a
24815
+ blank trace key means no lock); a template replaces that default rather than adding to it.
24816
+ The template is rendered from the record as it arrives at the import — before the import's
24817
+ own lookups and mapping run — so a key built from one of the import's lookup results
24818
+ (`{{lookup.<name>}}`) or from a mapped field renders empty, and an empty key is no lock at
24819
+ all; a field added upstream by a lookup step's response mapping is on the record and works.
24820
+ x-celigo-ai-guidance:
24821
+ - |-
24822
+ The lock is scoped to this import resource across every page and every concurrent run
24823
+ that submits through it — including other flows sharing the import — not to one page,
24824
+ and not to the connection: two different imports rendering the same key on the same
24825
+ connection still run in parallel. Records within a single page are
24826
+ sent one at a time regardless, so the lock matters when same-key records sit in different
24827
+ pages or runs.
24828
+ - |-
24829
+ With `ignoreExisting` and an ignore lookup, the existence lookup runs inside the lock:
24830
+ lookup-then-write for one key does not interleave with another record's lookup-then-write
24831
+ for the same key. That pairing is the duplicate-prevention pattern for destinations that
24832
+ can be queried but do not reject duplicates — key the lock on the business identifier the
24833
+ write is keyed on. With the check but no lock, concurrent same-key pages all read a miss
24834
+ before any write lands and all create. Connection `concurrencyLevel: 1` also closes that
24835
+ race for the import (measured on a single import: its pages ran one at a time and the
24836
+ same-key pages created once) — the lock is the per-key tool that leaves unrelated records
24837
+ parallel.
24838
+ - |-
24839
+ Key it on a field the record carries when it reaches this import (`{{{record.externalId}}}-GR`,
24840
+ or a field an upstream lookup step added through its response mapping), never on one of
24841
+ this import's own lookup results; the key must render non-empty for the records that
24842
+ matter, or those records have no lock.
24801
24843
  examples:
24802
24844
  - '{{record.customerId}}'
24845
+ - '{{{record.externalId}}}-GR'
24803
24846
  dataURITemplate:
24804
24847
  type:
24805
24848
  - string
@@ -24919,7 +24962,9 @@ components:
24919
24962
  Handlebars template that overrides how each record's unique trace key is generated,
24920
24963
  used to track records through the flow and match errors to records. Trace keys are
24921
24964
  capped at 256 characters; a longer key is stored truncated from the middle, keeping
24922
- the beginning and end of the value.
24965
+ the beginning and end of the value. The trace key is also the import's default
24966
+ concurrency lock key when `idLockTemplate` is not set; it never de-duplicates — a record
24967
+ that arrives twice with the same key is processed twice.
24923
24968
  x-celigo-ai-guidance:
24924
24969
  - |-
24925
24970
  ## CRITICAL: ONLY VALID WHEN ``oneToMany: true``
@@ -26679,6 +26724,7 @@ components:
26679
26724
  x-celigo-ai-guidance:
26680
26725
  - 'Set this only when the records sit inside an object (`{"data": {"items": [...]}}` → `data.items`). A sender that already posts a top-level array needs nothing here; a sender that posts an object with no path configured delivers exactly one record per request — the whole object — which is also the way to make one request one record on purpose.'
26681
26726
  - 'Records are paged per request at the export''s `pageSize` (default 20) before any hook, filter, or downstream step runs: a 25-element array is a page of 20 records followed by a page of 5, and a `preSavePage` hook receives one page (up to `pageSize` records) per call. A hook that groups rows by order therefore groups only the rows on the same page — an order whose rows span two pages comes out as two grouped records, the second after the first was already processed. To keep a request''s rows together, have the sender wrap them in an object (one record, rows under `record.<path>`) or raise `pageSize` above the largest request — never `groupByFields`, which the listener applies per page after paging.'
26727
+ - 'Requests that arrive together, or while the listener''s current run is still collecting, become additional pages of that same run; a request that arrives after the run has stopped collecting starts a new run. Nothing on the listener de-duplicates: a sender that fires the same event twice (a retry, a button clicked twice) delivers two records, and the step that writes the effect has to be made idempotent — `ignoreExisting` or a keyed upsert under the import''s `idLockTemplate`.'
26682
26728
  examples:
26683
26729
  - events
26684
26730
  - data.items
@@ -36912,7 +36958,9 @@ components:
36912
36958
  `{{join "_" record.customerId record.orderId}}`). When omitted, the system picks a unique
36913
36959
  field automatically. If a transform reshapes the data first, omit the `record.` prefix.
36914
36960
  Trace keys are capped at 256 characters; a longer key is stored truncated from the middle,
36915
- keeping the beginning and end of the value.
36961
+ keeping the beginning and end of the value. Downstream imports use the trace key as their
36962
+ default concurrency lock key (a blank key means no lock); it never de-duplicates — a record
36963
+ emitted twice with the same key is processed twice.
36916
36964
  examples:
36917
36965
  - '{{record.id}}'
36918
36966
  - '{{join "_" record.customerId record.orderId}}'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celigo/api-specs",
3
- "version": "0.2.112",
3
+ "version": "0.2.114",
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": "290f7bc1",
4
+ "iasCommit": "433e7903",
5
5
  "generatedAt": "2026-10-15"
6
6
  },
7
7
  "roots": {