> For the complete documentation index, see [llms.txt](https://developer.celigo.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.celigo.com/api/api-reference/exports-imports-and-tools/exports.md).

# Exports

Exports retrieve data from source systems — on a schedule, in delta mode, in real time, on demand, or as a file/blob transfer — package the results into ≤ 5-MB pages, and pass each page to downstream flow steps. Depending on configuration, an export surfaces in the Flow Builder as an export, a real-time listener, a file transfer, or a mid-flow lookup.

### Export schema

## The Export object

````json
{"openapi":"3.2.0","info":{"title":"Exports","version":"1.0.0"},"components":{"schemas":{"Export":{"type":"object","required":["_id","name","adaptorType","createdAt","lastModified"],"description":"Export object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ExportBase"},{"$ref":"#/components/schemas/ResourceResponse"},{"$ref":"#/components/schemas/IAResourceResponse"},{"type":"object","properties":{"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"apim":{"$ref":"#/components/schemas/APIM"},"apiIdentifier":{"type":"string","readOnly":true,"description":"API identifier assigned to this export."},"asynchronous":{"type":"boolean","readOnly":true,"description":"Server-managed execution-mode flag set on creation."},"__linkedLookupCacheIds":{"type":"array","readOnly":true,"description":"Lookup caches linked to this export, managed by the platform.","items":{"type":"string","format":"objectId"}},"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the source resource this export was created from."},"_templateId":{"type":"string","format":"objectId","readOnly":true,"description":"Template this export was created from."},"draft":{"type":"boolean","readOnly":true,"description":"When true, this export is in draft state and has not been confirmed."},"draftExpiresAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the draft version of this export expires."},"debugUntil":{"type":"string","format":"date-time","readOnly":true,"description":"Turns on step debug capture until this time: while it lies in the future, every\nrequest this export sends is recorded in the step's debug log\n(`GET /v1/{flows|apis|tools}/{containerId}/{_id}/requests`). Read-only on PUT —\nwritten only by `PATCH /v1/exports/{_id}` on `/debugUntil`, at most 3600 seconds\nahead (`422` `update_not_allowed` beyond that); `remove` stops capture."}}}]},"ExportBase":{"type":"object","description":"Writable export fields shared by the request and response schemas.","properties":{"name":{"type":"string","maxLength":100,"description":"Display name for the export, shown in the flow builder, job history, and error logs.\nDescriptive, unique names indicating the data source and purpose make large accounts easier to manage."},"description":{"type":["string","null"],"description":"Free-text summary of what the export retrieves and why. Shown in the UI and available\nto AI agents for context; has no effect on execution.","maxLength":5120},"_connectionId":{"format":"objectId","type":["string","null"],"description":"Connection this export uses to reach the source system. The connection's type must be\ncompatible with the export's `adaptorType` (e.g. an `HTTPExport` needs an `http` connection;\n`NetSuiteExport` and `NetSuiteHTTPExport` both need a `netsuite` connection).\nServer-required unless `type` is `webhook` or `simple` (those receive data instead of\nfetching it) or `adaptorType` is `TopicExport` (Event Streams listeners are delivered\nby the platform itself and must omit it). A `WebhookExport` saved without `type: webhook`\nis refused with 422 \"_connectionId is required unless type is [webhook|simple]\"; a webhook\nexport that does carry a connection saves and updates normally."},"adaptorType":{"type":"string","description":"Selects the adaptor technology that executes this export, which determines the compatible\nconnection types and which adaptor-specific configuration object must also be supplied\n(e.g. set `salesforce` when using `SalesforceExport`).","enum":["HTTPExport","FTPExport","AS2Export","S3Export","NetSuiteExport","NetSuiteHTTPExport","SalesforceExport","JDBCExport","RDBMSExport","MongodbExport","DynamodbExport","WrapperExport","SimpleExport","WebhookExport","FileSystemExport","TopicExport","RESTExport"]},"nsDomainType":{"type":"string","enum":["suitetalk","restlet"],"description":"Selects which NetSuite REST host a `NetSuiteHTTPExport` calls. The server derives the\nfull base URL from the connection's NetSuite account\n(`https://<account>.suitetalk.api.netsuite.com` or `https://<account>.restlets.api.netsuite.com`)\nand signs each request with the connection's token-based credentials, so `http.relativeURI`\ncarries the complete path starting at `/services/rest/...` or `/app/site/hosting/restlet.nl`.\nIgnored on other adaptor types."},"type":{"type":["string","null"],"description":"Operational mode of the export. When omitted, the export retrieves all available records —\nthe standard batch mode, which is also the right choice for parsing structured files\n(CSV/XML/JSON) into records. Most modes take their matching configuration object (e.g. a\n`webhook` object when `type` is `webhook`); an HTTP or SQL `delta` export saves without a\n`delta` object because its request carries `{{lastExportDateTime}}`, though\n`delta.startDate` sets its first window. It still needs `type: delta`: on an export\nwith no `type`, `{{lastExportDateTime}}` and `{{currentExportDateTime}}` render\nempty on every run, so a request that carries them goes out with blank values each time\n(live-verified 2026-10-22). Use `blob` only to transfer raw files without parsing their\ncontents.","enum":["webhook","test","delta","once","tranlinedelta","simple","blob","distributed","stream","all"]},"pageSize":{"type":["integer","null"],"description":"Number of records per page streamed to downstream flow steps when the export is the\nflow's source step. Pages are additionally capped at 5 MB regardless of record count, so\nvery large records may produce smaller pages. The server does not validate the value.\nA lookup (`isLookup: true`) runs to completion and hands every result to the incoming\nrecord as one response, capped at 5 MB in total, so the value does not change how many\nresults a lookup returns; a result set larger than that belongs in a standalone export\nstep. In a test run or preview the value also caps the records fetched (default 20).\nOn a webhook listener the records of each incoming request are paged separately, before\nany hook or downstream step runs.","default":20},"dataURITemplate":{"type":"string","description":"Handlebars template that builds a link back to each record in the source application's UI\n(e.g. `https://my.salesforce.com/lightning/r/Contact/{{record.Id}}/view`). The resolved\nURL is stored with error records in job history so users can jump straight to the record."},"traceKeyTemplate":{"type":["string","null"],"description":"Handlebars template that overrides how each record's unique trace key is generated, used\nto track records through the flow and match retries to prior errors (e.g.\n`{{join \"_\" record.customerId record.orderId}}`). When omitted, the system picks a unique\nfield automatically from the record's top level, by exact field name (`GET\n/v1/flows/{_id}/traceKey` serves the pattern). The rendered value of a template is stamped\non each record as `io_custom_trace_key` and travels downstream with it. If a transform\nreshapes the data first, omit the `record.` prefix. Trace keys are capped at 256 characters;\na longer key is stored truncated from the middle, keeping the beginning and end of the\nvalue. Downstream imports use the trace key as their default concurrency lock key (a blank\nkey means no lock); it never de-duplicates — a record emitted twice with the same key is\nprocessed twice."},"skipRetries":{"type":"boolean","description":"When true, the platform does not retain the source data needed to retry failed records,\nreducing stored data at the cost of being unable to reprocess errors from this export.\nShown as \"Do not store retry data\" in the UI. Defaults to false."},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"isLookup":{"type":"boolean","description":"When true, the export runs as a mid-flow lookup: it executes once per incoming record,\nusing the input record's fields to parameterize the request, and passes the results to\nsubsequent steps. When false, the export runs as a standalone data source. A lookup's\nresults are not paged through the flow: every page the adaptor fetches is merged into one\nresponse for the record, up to 5 MB, so `pageSize` does not limit or extend what a lookup\nreturns. A lookup that must return more than that is better modeled as a standalone\nexport step."},"groupByFields":{"$ref":"#/components/schemas/GroupBy"},"delta":{"$ref":"#/components/schemas/Delta"},"test":{"$ref":"#/components/schemas/Test"},"once":{"$ref":"#/components/schemas/Once"},"webhook":{"$ref":"#/components/schemas/Webhook"},"simple":{"$ref":"#/components/schemas/Simple"},"distributed":{"$ref":"#/components/schemas/Distributed"},"cdc":{"$ref":"#/components/schemas/Cdc"},"topic":{"$ref":"#/components/schemas/Topic"},"filesystem":{"$ref":"#/components/schemas/FileSystem"},"http":{"$ref":"#/components/schemas/Http"},"file":{"$ref":"#/components/schemas/File"},"salesforce":{"$ref":"#/components/schemas/Salesforce"},"as2":{"$ref":"#/components/schemas/AS2"},"dynamodb":{"$ref":"#/components/schemas/DynamoDB"},"ftp":{"$ref":"#/components/schemas/FTP"},"jdbc":{"$ref":"#/components/schemas/JDBC"},"mongodb":{"$ref":"#/components/schemas/MongoDB"},"netsuite":{"$ref":"#/components/schemas/NetSuite"},"rdbms":{"$ref":"#/components/schemas/RDBMS"},"s3":{"$ref":"#/components/schemas/S3"},"wrapper":{"$ref":"#/components/schemas/Wrapper"},"parsers":{"$ref":"#/components/schemas/Parsers"},"filter":{"description":"Filter applied to each record after the export's transform and before its preSavePage\nhook. Records that match continue through the flow; records that don't are silently\ndropped. Filter expressions reference the transformed record's fields.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"inputFilter":{"description":"Filter applied to incoming records before a lookup export queries the external system.\nOnly matching input records trigger lookup calls; non-matching records pass through the\nstep unenriched, which cuts unnecessary API traffic. Only relevant when `isLookup` is true.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"mappings":{"description":"Field mappings applied to each incoming record before a lookup HTTP request is made,\nreshaping the upstream record into the structure the lookup target API expects. Only\nsupported when `isLookup` is true and `adaptorType` is `HTTPExport` or\n`NetSuiteHTTPExport`; do not set it on source exports or other lookup adaptors.","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"transform":{"description":"Transformation that reshapes records before they leave this step — source exports\ntransform the retrieved records, lookup exports transform the lookup results. Commonly\nused to flatten nested lookup responses (e.g. `results[0].id` → `id`); merging results\nback into source records is handled separately by the flow's response mapping.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the export lifecycle for\ntransformations, validation, and filtering beyond what configuration alone can express.\nA hook name not listed here (an import's `preMap`, for example) is dropped on save\nwithout an error, so read `hooks` back after saving.","properties":{"preSavePage":{"type":"object","description":"Hook that runs on each page of records after the export's transform and output\nfilter, before the page is sent to downstream steps; commonly used to flatten\nstructures, drop unwanted records, or add computed fields. It receives the page as\n`options.data` (with `errors`, `pageIndex`, `configuration` and `settings`) and returns\n`{data, errors, abort, newErrorsAndRetryData}`. Return that object, not the array: a\nhook that returns `options.data` itself, or a filtered copy, drops the page without an\nerror, and the run completes with 0 records, 0 errors and 0 pages. An exception thrown\nhere fails every record on that page, each with its own retry data, and later pages\nstill run.","properties":{"function":{"type":"string","description":"Function to invoke within the referenced script."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`. Must be a script in this\naccount: any other id fails the save with 422 (\"Only a script owned by the user or\na script shared by the Integration App can be used at hooks.preSavePage._scriptId.\")."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments."},"configuration":{"type":"object","description":"Static parameters passed to the hook function at runtime, letting one script be\nreused across exports with different settings."}}},"preParse":{"type":"object","description":"Hook that runs on file-based exports after the file is downloaded and before it is\nparsed: it receives the raw file text as `preParseData` together with `fileMeta` and\nreturns both, modified or not, for the configured parser to read. Used to normalize a\nnon-standard file — fix a header row, strip a preamble, convert a format — before\nparsing; an exception thrown here fails the run.","properties":{"function":{"type":"string","description":"Function to invoke within the referenced script."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments."},"configuration":{"type":"object","description":"Static parameters passed to the hook function at runtime, letting one script be\nreused across exports with different settings."}}}}},"settingsForm":{"$ref":"#/components/schemas/Form"},"settings":{"$ref":"#/components/schemas/Settings"},"mockOutput":{"$ref":"#/components/schemas/MockOutput"},"_ediProfileId":{"type":"string","format":"objectId","description":"EDI profile this export uses to parse incoming X12 or EDIFACT documents into structured\nJSON — it supplies the envelope qualifiers, delimiters, version, and validation rules.\nSet it when the export parses or validates EDI files (a save with a `delimited/x12` file\ndefinition at `version` `\"2\"` and no profile fails with 422 `missing_ediProfile`); omit\nit otherwise. Accepted on the file-reading exports — FTP, AS2, S3 and HTTP file mode\n(`http.type: \"file\"`)."},"_postParseListenerId":{"type":"string","format":"objectId","description":"Webhook export invoked once per file after EDI parsing, on both success and failure\n(errors are included in the payload when parsing fails). Primarily used to send\nfunctional acknowledgements (997/999) to trading partners. Accepted on the file-reading\nexports that parse EDI — FTP, AS2, S3 and HTTP file mode (`http.type: \"file\"`)."},"externalId":{"type":["string","null"],"description":"External identifier for correlating the export with a record in another system."},"_integrationId":{"type":["string","null"],"format":"objectId","description":"Not stored on an export: a value sent on create or on a full update is dropped, and a GET\ndoesn't return the field. An export isn't owned by an integration; to find the flows and\nother resources that use it, call `GET /v1/exports/{_id}/dependencies`."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this export was created from, set when the export is part of an installed\nintegration app. The app's install sets it: a create that sends it is refused with 403\n(\"Cannot create an Integration App resource\")."},"unencrypted":{"type":"object","description":"Custom configuration values stored without encryption and returned in API responses."},"useTechAdaptorForm":{"type":"boolean","description":"When true, the UI presents the full technical adaptor form for this export instead of\nthe simplified assistant form."},"rawData":{"type":"string","description":"Key referencing the raw sample payload captured for preview and testing."},"preSave":{"$ref":"#/components/schemas/PreSave"},"assistant":{"type":"string","description":"Identifier for the connector assistant used to configure this export. For the cloud\nfile providers it also selects the provider-specific location rules —\n`azuredatalakestoragegen2` (container in `file.directory.name`, path in\n`http.relativeURI`) and `googlecloudstorage` (bucket in `file.directory.storageRootId`,\nprefix in `file.directory.id`)."},"assistantMetadata":{"type":["object","string"],"additionalProperties":true,"description":"Metadata associated with the connector assistant configuration."},"sampleData":{"type":["string","object","array","null"],"description":"Sample data payload used for previewing and testing the export."},"sampleHeaders":{"type":"array","description":"Sample HTTP headers used for previewing and testing the export.","items":{"type":"object","properties":{"name":{"type":"string","description":"Header name."},"value":{"type":"string","description":"Header value."}}}},"sampleQueryParams":{"type":"array","description":"Sample query parameters used for previewing and testing the export.","items":{"type":"object","properties":{"name":{"type":"string","description":"Query parameter name."},"value":{"type":"string","description":"Query parameter value."}}}}}},"OneToMany":{"type":"boolean","description":"When true, the step runs once per child record instead of once per incoming record.\n`pathToMany` names the array field that holds the children inside an object record; when\nthe incoming record is itself an array (grouped or row-based data), leave `pathToMany` blank\nand each element becomes a record. The fan-out is scoped to this step: afterwards the children\nare re-joined into the record's original shape — the object with its array, or the array of rows —\ncarrying any response-mapping enrichment, and that re-joined record is what the next step\nreceives. Not for locating records in an export's HTTP response; use\n`http.response.resourcePath` for that. Applies to steps that receive a record — imports and\nlookups (an export referenced as a page processor of a flow, API, or Tool). A standalone export\n(a flow's page generator) has no incoming record, so the fields are saved but have no effect on\nthe records it produces; to emit one record per array element from a standalone export, use a\n`hooks.preSavePage` script, or for a file-definition export make the repeating segment the\nrecord boundary in the definition's rules.\n","default":false},"PathToMany":{"type":"string","description":"Path to the array of child records inside an object record when `oneToMany` is true, in dot\nnotation (`items`, `lines.lineItems`). Leave blank when the incoming record is itself an array\n(grouped or row-based data) — each element is then a child record. A path that does not resolve\nto an array processes zero records and reports success. Read only on steps that receive a\nrecord (imports, lookup exports); on a standalone export it is saved and ignored — see `oneToMany`.\n"},"GroupBy":{"type":"array","description":"Specifies which fields to use for grouping records in the export results. When configured, records with\nthe same values in these fields will be grouped together and treated as a single record by downstream\nsteps in your flow.\n\nFor example:\n- Group sales orders by customer ID to process all orders for each customer together\n- Group journal entries by accounting period to consolidate related transactions\n- Group inventory items by location to process inventory by warehouse\n\nWhen grouping is used, the export's page size determines the maximum number of groups per page, not individual\nrecords. Note that effective grouping typically requires that records with the same group field values appear\ntogether in the export data.\n\nNot a webhook listener feature: on a `webhook` export the platform applies it only within each page, after\nthe request has been paged, and only to adjacent rows — the field is not offered for listeners in the UI.\n","items":{"type":"string"}},"Delta":{"type":"object","description":"Configures incremental exports that retrieve only records created or modified since the\nflow's delta checkpoint; omit it for other export types. Adaptors that compare a record field\nname it in `dateField`, while HTTP exports and SQL queries carry {{lastExportDateTime}} in the\nrequest and save without a delta object. Each run's window runs from {{lastExportDateTime}}\n(the checkpoint) to {{currentExportDateTime}} (the time the run started). A first run is not a\nfull export: with no `startDate`, its {{lastExportDateTime}} is the run's own timestamp, so it\npicks up no earlier changes. The checkpoint moves to a run's window end only when the run\nexports records, so a run that exports nothing leaves it where it was, and after a failed run\nthe next run reuses the last checkpoint. On success, when `POST /v1/flows/{_id}/run` sends\n`export.endDate`, the flow's checkpoint is left at that value, so an end date in the past\nmoves the next run's lower bound back.","properties":{"dateField":{"type":"string","description":"Record timestamp field(s) compared against the last successful run time to identify\nchanged records. Accepts a single field or multiple comma-separated fields, processed\nsequentially — useful when different operations update different timestamp fields.\nIf the flow's own downstream steps update the exported records, add export criteria\nthat exclude already-processed records (or use a creation-time field for process-once\nflows) so each run doesn't re-export what the previous run wrote. Not supported on\nHTTP exports: embed {{lastExportDateTime}} in the relativeURI or body instead; an HTTP\nexport saved with dateField is refused with 422 \"Unsupported dateField in\nexport.delta.dateField\". For Salesforce this\nfield is required and defaults to the standard timestamp fields (LastModifiedDate,\nCreatedDate, SystemModstamp, LastActivityDate, LastViewedDate, LastReferencedDate) plus\nany custom timestamp fields."},"dateFormat":{"type":"string","description":"Moment.js format string applied to both delta variables, {{lastExportDateTime}} and\n{{currentExportDateTime}}, in HTTP requests and SQL queries alike. Leave unset unless the\nsource system requires a non-ISO8601 format; by default both render as ISO 8601 UTC with\nmilliseconds. Date-only formats truncate the time portion, widening the filter window."},"lagOffset":{"type":"integer","description":"Buffer in milliseconds subtracted from the last successful run timestamp, creating an\noverlapping window that catches records still propagating when the previous run executed.\nSet it when records created or modified near the run time are occasionally skipped due to\nreplication or indexing delays. Keep it as low as possible — larger values reprocess\nredundant records. A negative value shifts the window forward (look-ahead) instead of back."},"startDate":{"type":["string","null"],"format":"date-time","description":"Lower-bound cutoff for the first run. Omitting it doesn't export history: the first run\nthen starts at its own timestamp, an empty window. Set it to the earliest change the first\nrun should pick up. Later runs start from the flow's checkpoint, the end of the last run\nthat exported records."}}},"Test":{"type":"object","description":"Configures test exports that cap the number of records retrieved, for safely developing and\nvalidating against small data samples. Required when the export's type is \"test\"; omit for\nother export types. A test export behaves like a standard export in every other way (filters,\npagination, processing) and stores no state between runs.","properties":{"limit":{"type":"integer","default":1,"description":"Caps the total records a test run processes, counted on top-level records before\noneToMany processing; the maximum exists to prevent accidentally processing large\ndatasets during development. The cap applies across pages — processing stops once the\nlimit is reached regardless of pageSize. When transitioning to production, leave this in\nplace and change the export's type field; the limit then no longer applies.","minimum":1,"maximum":100}}},"Once":{"type":"object","description":"Configures flag-based exports that skip records an earlier run already processed. Required\nwhen the export's type is \"once\"; omit for other export types. Each run retrieves records\nwhere the tracking boolean field is false, then sets that field to true after successful\nprocessing so later runs skip them; if a run fails, the flags are left unchanged and the\nrecords are retried automatically on the next run.","properties":{"booleanField":{"type":"string","description":"API field name of the boolean/checkbox in the source system that tracks processed\nrecords: the export selects only records where this field is false, then sets it to true\nin batches after each successfully processed page. The field must be writeable by the\nexport's connection. Ensure no other process updates the same field — use a separate\nflag per export process — or records may be skipped unexpectedly."}}},"Webhook":{"type":"object","description":"Configuration for webhook listeners that receive data through incoming HTTP requests. Required when the export's `type` is `webhook`; omit for other export types. Senders POST to the export's listener URL, `https://api.integrator.io/v1/exports/{_id}/data` (or the same path on the account's regional API host); the platform validates each request using the configured `verify` method, passes the payload to subsequent flow steps, and returns a configurable HTTP response to the caller. A request is answered 204 by default when accepted, 401 when it fails verification, and 403 `unable_to_run_flows` while every flow the export feeds is disabled; a refused request is not queued, so the sender must resend it.","properties":{"provider":{"type":"string","description":"Source application sending the webhook, used to pre-configure security settings and payload parsing for that platform. Use `custom` (the default when omitted) for unlisted providers or when you need full manual control over the security configuration. Provider-specific choices may require credentials (tokens, keys) mandated by that platform.","enum":["github","shopify","travis","travis-org","slack","dropbox","onfleet","helpscout","errorception","box","stripe","aha","jira","pagerduty","postmark","mailchimp","intercom","activecampaign","segment","recurly","shipwire","surveymonkey","parseur","mailparser-io","hubspot","integrator-extension","custom","sapariba","happyreturns","typeform"]},"verify":{"type":"string","description":"Verification method applied to every incoming request before processing; required for all webhook exports. Each method needs companion fields: `hmac` requires `key`, `algorithm`, `encoding`, and `header` (except on connector-backed webhooks, where the connector definition supplies them); `token` requires `token`, with `tokenLocation` defaulting to `body` and selecting which location-specific field applies; `basic` requires `username` and `password`; `secret_url` requires `token`. Prefer `hmac` when the source system supports it — it is the most secure option; `secret_url` relies only on URL obscurity and suits non-sensitive data or testing.","enum":["token","hmac","basic","secret_url"]},"token":{"type":"string","description":"Shared secret used when `verify` is `token` or `secret_url`. For `token` verification, the value found at `path` in each request must exactly match (case- and whitespace-sensitive) or the request is rejected with a 401. For `secret_url`, the token is embedded in the webhook URL to create a hard-to-guess endpoint — generate a random, high-entropy value and treat it as a sensitive credential."},"algorithm":{"type":"string","description":"Hashing algorithm used to validate HMAC signatures when `verify` is `hmac`. Must match the algorithm the webhook sender uses — a mismatch causes every request to be rejected. Use `sha256` unless the provider explicitly requires another value.","enum":["sha1","sha256","sha384","sha512"]},"encoding":{"type":"string","description":"Encoding of the HMAC signature value when `verify` is `hmac`. Must match the encoding the webhook sender uses — a mismatch causes requests to be rejected with a 401 even when the signature is otherwise correct. With `provider: custom`, `hex` is compared as lowercase hex: an uppercase hex signature is rejected.","enum":["hex","base64"]},"key":{"type":"string","description":"Secret used to validate signatures when `verify` is `hmac`. The system computes a signature of the request body using this key and the configured `algorithm`, then compares it with the signature sent in the request header named by `header`. Treat it as a highly sensitive credential — never expose it in repositories or logs."},"header":{"type":"string","description":"Name of the request header that carries the HMAC signature when `verify` is `hmac`. Must match the header name the webhook sender uses (header names are case-insensitive); requests without this header are rejected with a 401. With `provider: custom` the header value must be the bare digest in the configured `encoding`: a value with a `sha256=` prefix is rejected with a 401."},"tokenLocation":{"type":"string","enum":["body","header","queryParam"],"default":"body","description":"Where the verification token is found when `verify` is `token`. Each location uses a different companion field: `body` uses `path`, `header` uses `tokenHeaderName` (and `tokenHeaderScheme`), `queryParam` uses `tokenQueryParamName`."},"path":{"type":"string","description":"JSON path into the request body holding the verification token when `verify` is `token` and `tokenLocation` is `body` (e.g. `meta.token`). The value at this path must exactly match `token` or the request is rejected."},"tokenHeaderName":{"type":"string","description":"Request header carrying the verification token when `verify` is `token` and `tokenLocation` is `header`."},"tokenHeaderScheme":{"type":"string","enum":["bearer","custom","none"],"default":"bearer","description":"Scheme prefixing the token in the header when `tokenLocation` is `header`."},"customTokenScheme":{"type":"string","description":"Custom header scheme prefix used when `tokenHeaderScheme` is `custom`."},"tokenQueryParamName":{"type":"string","description":"Query parameter carrying the verification token when `verify` is `token` and `tokenLocation` is `queryParam`."},"_httpConnectorId":{"type":"string","format":"objectId","description":"HTTP connector backing this listener when the webhook is provided by an assistant/connector (e.g. Slack); set by the platform for connector-backed listeners."},"requestMediaType":{"type":"string","enum":["json","xml","csv","urlencoded","plaintext"],"description":"Overrides how the incoming request body is parsed when the provider sends a non-standard content type. When omitted, the body is parsed by its Content-Type header."},"pathToRecords":{"type":"string","description":"JSON path into an object payload to the array whose elements become the records. When omitted, a top-level JSON array is emitted as one record per element and an object payload is emitted as a single record."},"includeParentData":{"type":"boolean","description":"When true and `pathToRecords` is set, each emitted record also carries the surrounding parent fields from the payload."},"username":{"type":"string","description":"Username half of the credentials when `verify` is `basic`. Incoming requests must include an `Authorization: Basic` header carrying the base64-encoded `username:password` pair. Use only over HTTPS to prevent credential interception."},"password":{"type":"string","description":"Password half of the credentials when `verify` is `basic`, validated together with `username` from the request's `Authorization` header. Use a strong, unique value and treat it as a sensitive credential."},"successStatusCode":{"type":"integer","description":"HTTP status code returned to the caller after successful processing; must be a valid 2xx code. The default 204 returns no response body and causes `successBody` to be ignored — set 200 or 202 when the caller needs a response body or expects a specific code.","default":204},"successBody":{"type":"string","description":"Response body returned to the caller after successful processing; ignored when `successStatusCode` is 204. Content type is set by `successMediaType`, and the value can be static text or structured JSON/XML, including handlebars expressions for dynamic values."},"successMediaType":{"type":"string","description":"Sets the Content-Type header on successful webhook responses. Only takes effect when a `successBody` is returned (a status code other than 204), and must match the actual format of that body.","default":"json","enum":["json","xml","plaintext"]},"successResponseHeaders":{"type":"array","description":"Custom headers added to successful webhook responses — for example CORS headers or correlation IDs. Headers defined here take precedence over automatically set headers such as Content-Type, and values support handlebars expressions for dynamic content.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the header to set on successful webhook responses; headers defined here take precedence over automatically set headers such as Content-Type."},"value":{"type":"string","description":"Value sent for the header; supports handlebars expressions for dynamic content."}}}},"challengeResponseHeaders":{"type":"array","description":"Custom headers returned for webhook subscription verification (challenge) requests, which providers send before delivering real events. Required header values vary by provider — consult the provider's documentation, since incorrect challenge headers can prevent the subscription from being verified.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the header to set on challenge (subscription verification) responses."},"value":{"type":"string","description":"Value sent for the header; consult the provider's documentation for required challenge header values."}}}},"challengeSuccessBody":{"type":"string","description":"Response body returned for webhook subscription verification (challenge) requests. Many providers require echoing back a challenge value from the request, which handlebars expressions can access — for example `{{hub.challenge}}` (Facebook/Instagram) or `{\"challenge\":\"{{challenge}}\"}` (Slack). An incorrect challenge response prevents the subscription from being verified."},"challengeSuccessStatusCode":{"type":"integer","description":"HTTP status code returned for webhook subscription verification (challenge) requests. Most providers expect the default 200; change it only when the provider's verification explicitly requires a different code.","default":200},"challengeSuccessMediaType":{"type":"string","description":"Sets the Content-Type header on challenge responses. Must match both the format of `challengeSuccessBody` and the provider's requirements — for example, Slack expects `json` while Facebook/Instagram verification expects `plaintext`.","default":"json","enum":["json","xml","plaintext"]}},"if":{"properties":{"verify":{"const":"hmac"}},"required":["verify"],"not":{"required":["_httpConnectorId"]}},"then":{"required":["key","algorithm","encoding","header"]},"else":{"if":{"properties":{"verify":{"const":"token"}},"required":["verify"]},"then":{"required":["token"],"if":{"properties":{"tokenLocation":{"const":"body"}},"required":["tokenLocation"]},"then":{"required":["path"]},"else":{"if":{"properties":{"tokenLocation":{"const":"header"}},"required":["tokenLocation"]},"then":{"required":["tokenHeaderName"]},"else":{"if":{"properties":{"tokenLocation":{"const":"queryParam"}},"required":["tokenLocation"]},"then":{"required":["tokenQueryParamName"]},"else":{"required":["path"]}}}},"else":{"if":{"properties":{"verify":{"const":"basic"}},"required":["verify"]},"then":{"required":["username","password"]},"else":{"if":{"properties":{"verify":{"const":"secret_url"}},"required":["verify"]},"then":{"required":["token"]}}}}},"Simple":{"type":"object","description":"Configuration for `simple` (data-loader) exports — exports that accept manually uploaded\nfiles through the data-loader UI instead of fetching data from a connection. Relevant only\nwhen the export's `type` is `simple`; the uploaded content is parsed using the same file\nsettings as file-based exports.","properties":{"file":{"$ref":"#/components/schemas/File"}}},"File":{"type":"object","description":"Controls how files are parsed, filtered, and processed across all file-based exports\n(FTP/SFTP, Amazon S3, simple file uploads, and other file sources). The type field selects\nthe file format and determines which format-specific object (csv, json, xlsx, xml, or\nfileDefinition) must be configured; the output field selects whether files are parsed into\nrecords, transferred as blobs, or listed as metadata only. The filter object selectively\nskips files before processing.","properties":{"encoding":{"type":"string","description":"Character encoding used to read and parse file content. If the encoding is unknown, try\nutf8 first (the default), then win1252 for Western-language files with garbled\ncharacters; consider the geographic origin of the data when selecting.","enum":["utf8","win1252","utf-16le","gb18030","macroman","iso88591","shiftjis"]},"type":{"type":"string","description":"Format of the files being processed; determines which format-specific configuration\nobject (csv, json, xlsx, xml, or fileDefinition) must be populated — other format\nobjects are ignored. Required for all file-based exports except blob exports (export\ntype \"blob\" or output \"blobKeys\").","enum":["csv","json","xlsx","xml","filedefinition"]},"output":{"type":"string","description":"Processing mode for retrieved files: parse contents into records, transfer files as\nunparsed blobs, or return only file metadata. Determines what data is passed to\nsubsequent flow steps.","enum":["records","metadata","blobKeys"]},"skipDelete":{"type":"boolean","default":false,"description":"When true, source files remain on the file server after processing; when false (the\ndefault), files are deleted after successful processing. Files that fail processing are\nnever deleted, regardless of this setting. Enable retention when files must be processed\nby other flows or systems, or kept for compliance."},"compressionFormat":{"type":"string","description":"Compression format of incoming files, which are decompressed before any other processing\n(parsing, filtering). Set this only when the source always delivers compressed files —\nif a file marked as compressed is not actually compressed, processing fails. Leave unset\nwhen files arrive uncompressed or only sometimes compressed.","enum":["gzip","zip"]},"purgeInternalBackup":{"type":"boolean","description":"When true, Celigo keeps no internal backup copies of files processed by this export;\nwhen false (the default), copies are retained for your account's retention period and\nare available for reprocessing or troubleshooting. Applies only to this export and only\nto Celigo's internal copies — source files are governed by skipDelete. Enable for highly\nsensitive data or zero-retention policies; without backups, recovery may require\nre-obtaining files from the source system."},"decrypt":{"type":"string","description":"Decryption applied to incoming files before any other processing; decryption runs before\ndecompression, and a decryption failure fails the file's processing entirely. The\nconnection must already be configured with the private key (and passphrase, if\napplicable) matching the public key used to encrypt the files. Only PGP/GPG encryption\nis currently supported.","enum":["pgp"]},"batchSize":{"type":["integer","null"],"description":"Number of files retrieved per batch; if a batch fails, the whole batch is retried\n(stored as `null` when not configured). Use\nlower values (10-50) for large files to reduce timeout and memory pressure, and higher\nvalues for many small files to improve throughput. Controls file retrieval only — record\npaging is governed by the export's pageSize.","maximum":1000},"sortByFields":{"type":"array","description":"Sorts the records parsed from each file before they are processed, establishing a\ndeterministic processing order (for example, chronological or priority-based). Sorting\nhappens after parsing but before any filtering or grouping, and does not modify the\nsource files. Sorting by the same fields used in groupByFields improves grouping\nperformance.","items":{"type":"object","properties":{"field":{"type":"string","description":"Record field to sort by; use dot notation for nested properties (e.g.\ncustomer.name). Field names are case-sensitive."},"descending":{"type":"boolean","description":"When true, sorts this field in descending order (newest/highest first); when false\nor omitted, sorts ascending. Directions can be mixed across fields in a\nmulti-field sort."}}}},"groupByFields":{"$ref":"#/components/schemas/GroupBy"},"groupEmptyValues":{"type":"boolean","description":"When true, records whose groupByFields values are empty are still grouped together rather\nthan each forming its own group; when false (the default), empty-keyed records are not\ngrouped. Only relevant when groupByFields is set."},"csv":{"type":"object","description":"Parsing settings for delimiter-separated text files (CSV, TSV, pipe-delimited, and\nsimilar). Configure when type is \"csv\".\n\nRequired when type is csv.","properties":{"columnDelimiter":{"type":"string","description":"Character sequence separating fields within each row; comma when omitted. Use \"\\t\"\nfor tab-delimited files; European-locale exports often use semicolons. A wrong\ndelimiter doesn't fail the parse: with a header row, each row becomes one field named\nafter the whole header line (`{\"order_id;sku;qty\": \"1001;TEE-M;2\"}`), so the failure\nshows up in later steps."},"rowDelimiter":{"type":"string","description":"Character sequence marking the end of each record; auto-detected from file content\nwhen omitted. Set explicitly (\"\\n\", \"\\r\\n\", or \"\\r\") only when auto-detection fails,\nsuch as for files with mixed line endings — an incorrect value merges or splits\nrecords."},"hasHeaderRow":{"type":"boolean","description":"When true (the default), the first row is read as field names rather than data, and\nthose names are available in mappings. When false, every row is treated as a data\nrecord and fields are referenced by position."},"trimSpaces":{"type":"boolean","description":"When true, removes leading and trailing whitespace from every field value during\nparsing; when false (the default), whitespace is preserved exactly as in the source.\nHeader row values are always trimmed regardless of this setting, and spaces between\nwords are never affected."},"rowsToSkip":{"type":"integer","description":"Number of rows at the top of the file to ignore before parsing begins — useful for\nreport titles, timestamps, or other metadata above the data. The header row (when\nhasHeaderRow is true) is expected after the skipped rows."},"disableQuoteAndStripEnclosingQuotes":{"type":"boolean","description":"When true, disables CSV quote processing: quotes are treated as literal characters,\nenclosing quotes are stripped, and delimiters inside quoted text split the field.\nWhen false (the default), standard RFC 4180 quoting applies and quoted fields\nprotect embedded delimiters. Enable only for files with non-standard or malformed\nquoting; review this setting first when field counts vary unexpectedly between rows."},"keyColumns":{"type":"array","description":"Field names whose values together identify a record's group when consecutive rows\nshare the same key — used to merge multi-line records (e.g. an order header repeated\nacross line-item rows) into a single record. Leave empty for one record per row; the\ncolumns must exist in the parsed header.","items":{"type":"string"}}}},"json":{"type":"object","description":"Parsing settings for JSON files. Configure when type is \"json\". resourcePath locates the\narray of records when they are nested inside a container object; malformed JSON fails\nthe entire file's processing.\n\nRequired when type is json.","properties":{"resourcePath":{"type":"string","description":"Dot-notation path to the array of records within the JSON structure (e.g.\n\"response.data.customers\"); leave empty when the file's root is already the record\narray. The path must resolve to an array of objects — array indexing and wildcards\nare not supported. A path that resolves to nothing produces zero records without\nraising an error."}}},"xlsx":{"type":"object","description":"Parsing settings for Microsoft Excel (.xlsx) workbooks. Configure when type is \"xlsx\".\nCalculated cell values are extracted rather than formulas; legacy .xls files are not\nsupported — use the modern Open XML format.\n\nRequired when type is xlsx.","properties":{"hasHeaderRow":{"type":"boolean","description":"When true (the default), the first row is read as field names rather than data;\nblank header cells are auto-named and duplicate names are made unique with suffixes.\nWhen false, every row is treated as data and fields receive generic positional names\n(Column1, Column2, ...)."}}},"xml":{"type":"object","description":"Parsing settings for XML documents. Configure when type is \"xml\". resourcePath is an\nXPath expression selecting the elements treated as records; namespaces are handled\nautomatically.\n\nRequired when type is xml.","properties":{"resourcePath":{"type":"string","description":"XPath expression selecting the elements treated as records — each matching element\nbecomes one record, with its child elements as fields. Required for XML parsing;\nthere is no default. Use an absolute path (/Root/Order) when the structure is fixed,\n//Element to match at any depth, or predicates (//Element[@type=\"product\"]) to\nfilter; XPath is case-sensitive, and a non-matching path yields zero records."}}},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"},"fileDefinition":{"type":"object","description":"Parsing via a predefined file definition resource, for formats the standard parsers\ncannot handle — fixed-width files, EDI documents (X12, EDIFACT), and multi-record-type\nor proprietary formats. Configure when type is \"filedefinition\".\n\nRequired when type is filedefinition.","properties":{"_fileDefinitionId":{"type":"string","format":"objectId","description":"File definition resource containing the parsing rules for this format. Must\nreference an existing, accessible file definition — never guess or fabricate the ID.\nDefinitions are reusable across exports, so changing one affects every export that\nuses it."},"allowPartialSuccess":{"type":"boolean","description":"When true, a file whose records partially fail parsing against the file definition\nstill emits the records that did parse, instead of failing the entire file. Applies to\nfile-definition (fixed-width, EDI) parsing only. For X12 and EDIFACT the unit is the\ntransaction set (ST–SE, UNH–UNT), and it takes effect only with an EDI profile linked:\na failed set is set aside and reported as an error while the file's other sets go on\nas records. Without this flag, one failed set fails the whole file."}}},"filter":{"description":"Selects which files are processed: files matching the filter criteria are exported,\nand non-matching files are skipped entirely before processing begins. Filterable\nfields come from each file's listing entry: the name as `fileName`, the size as\n`filesize`, and the modified time as `lastModifiedTime` on FTP, SFTP and S3 or\n`modifiedTime` on cloud-storage folders (`http.type: \"file\"`)\n(e.g. [\"endswith\", [\"string\", [\"extract\", \"fileName\"]], \".csv\"]). There is no\n`lastmodified` key.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"backupPath":{"type":"string","description":"Path where backup copies of source files are stored before processing."},"includeSubdirectories":{"type":"boolean","default":false,"description":"When true, an Azure Data Lake Storage Gen2 transfer (`assistant: azuredatalakestoragegen2`,\n`http.type: file`) also picks up files in the subdirectories below the directory path in\n`http.relativeURI`; when false or omitted only that directory's own files are transferred."},"directory":{"type":"object","required":["pathMode"],"description":"Structured source location for cloud file-provider sources (Google Drive shared drives,\nBox, Dropbox, Google Cloud Storage, Azure Data Lake Storage Gen2), replacing the flat path\nused by classic FTP/S3 sources. Select the location by folder ID or by a path relative to\na configured storage root via pathMode. For the drive-style providers, directoryId mode\naddresses the folder by id alone — the flat relative-path field is not consulted at\nruntime. Google Cloud Storage puts the bucket in `storageRootId` and the object prefix in\n`id` with `pathMode: directoryId`; Azure Data Lake Storage Gen2 puts the container in\n`name` and the directory path in `http.relativeURI`.","properties":{"pathMode":{"type":"string","enum":["relativePath","directoryId"],"description":"How the directory is addressed — by provider folder ID, or by a path relative to a configured storage root."},"id":{"type":"string","description":"Provider-native folder ID of the source directory (e.g. a Google Drive folder ID); set\nwhen pathMode is directoryId. This is the file provider's own ID, not a Celigo resource ID.\nFor Google Cloud Storage it is the object prefix inside the bucket, ending with `/`."},"name":{"type":"string","description":"Display name of the folder identified by id, kept for readability in the UI; the\nfolder browser fills it when the location is picked visually. For Azure Data Lake\nStorage Gen2 it is the authoritative container name instead — the form saves the\ncontainer here and no id."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root (e.g. a Google Drive shared-drive ID) the relative\npath resolves against; set when pathMode is relativePath. The provider's own ID, not a Celigo ID.\nFor Google Cloud Storage it is the bucket name."},"storageRootName":{"type":"string","description":"Display name of the storage root identified by storageRootId, retained for reference in the UI."}}},"backupDirectory":{"type":"object","required":["pathMode"],"description":"Structured destination for backup copies of source files on cloud file providers — the\nfile-provider counterpart to backupPath. Addressed the same way as directory.","properties":{"pathMode":{"type":"string","enum":["relativePath","directoryId"],"description":"How the backup location is addressed — by provider folder ID, or by a path relative to a configured storage root."},"id":{"type":"string","description":"Provider-native folder ID of the backup directory; set when pathMode is directoryId."},"name":{"type":"string","description":"Display name of the backup folder identified by id, kept for readability in the UI;\nthe folder browser fills it when the location is picked visually."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root the backup relative path resolves against; set when pathMode is relativePath."}}}}},"IncludeParentData":{"type":"boolean","description":"When true and a path to records is configured on the export (`pathToRecords` for MongoDB and DynamoDB, `http.response.resourcePath` for HTTP, `file.json`/`file.xml` `resourcePath` for file-based exports), the data outside that path is attached to each extracted record as a `_PARENT` object, keeping the surrounding envelope available to downstream steps. When omitted or false, records are emitted without `_PARENT`, exactly as before. Constraints: on HTTP exports this option cannot be combined with `useXpathParser: true` (rejected at save with `EXPORT_INCLUDE_PARENT_DATA_XPATH_UNSUPPORTED`), and XML responses must resolve to the default converted-JSON parse mode — a run whose XML parser resolves to DOM/XPath mode (for example, because a connection-level field uses an advanced XPath expression) fails at start with the same error rather than silently omitting `_PARENT`. On file-based exports, it requires `file.type` of `json` with `file.json.resourcePath` configured — XML file envelope capture is not yet supported (the streaming XML parser emits no envelope event; XML support arrives with a parsers-package enhancement) — and any other file configuration is rejected at save with `EXPORT_INCLUDE_PARENT_DATA_UNSUPPORTED_FILE_TYPE`."},"Filter":{"type":"object","description":"Configuration for selectively processing records based on specified criteria. This object enables\nprecise control over which items are included or excluded from processing operations.\n\n**Filter behavior**\n\nWhen configured, the filter is applied before processing begins:\n- Items that match the filter criteria are processed\n- Items that don't match are completely skipped\n- No partial processing is performed\n\nSend the whole object. A rules array sent alone as the `filter` value is not\nstored, and a full update of an export that carries one removes the filter the\nexport had, with no error.\n\n**Implementation approaches**\n\nThere are two distinct filtering mechanisms available:\n\n**Rule-Based Filtering (`type: \"expression\"`)**\n- **Best For**: Common filtering patterns based on standard attributes\n- **Capabilities**: Filter by names, values, dates, numerical ranges, text patterns\n- **Advantages**: Declarative, no coding required, consistent performance\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear, static criteria for selection\n\n**Script-Based Filtering (`type: \"script\"`)**\n- **Best For**: Complex logic, dynamic criteria, or business rules\n- **Capabilities**: Full programmatic control, access to complete metadata\n- **Advantages**: Maximum flexibility, can implement any filtering logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Simple rules aren't sufficient or logic needs to be dynamic\n","properties":{"type":{"type":"string","description":"Determines which filtering mechanism to use. This choice affects which properties\nmust be configured and how filtering logic is implemented.\n\n**Available types**\n\n**Rule-Based Filtering (`\"expression\"`)**\n- **Required Config**: The `expression` object with rule definitions\n- **Behavior**: Evaluates declarative rules against item attributes\n- **Best For**: Common patterns like name matching, date ranges, value limits\n- **Advantages**: Simpler to configure, no custom code required\n\n**Script-Based Filtering (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to determine which items to process\n- **Best For**: Complex conditions, business logic, dynamic criteria\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard filtering needs (name, size, date), use `\"expression\"`\n2. For complex logic or conditions not covered by expressions, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based filtering. This object enables filtering\nitems based on common attributes without requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define filtering rules that\ncan match against item attributes like name, type, value, date, and other properties.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Rules can be combined with AND/OR logic\n- Each rule can check a specific attribute\n- Multiple conditions can be applied (ranges, pattern matching, exact matches)\n\n**Common filter patterns**\n\n1. **Pattern matching**: Regular expressions with `matches`\n2. **Value range filtering**: Numbers between min and max values\n3. **Date range filtering**: Items created/modified within specific time ranges\n4. **Status checking**: Items with specific status values or properties\n\nFor AI agents: Rule-based filtering should be your first choice when the filtering criteria\ncan be expressed in terms of standard attributes. Only use script-based filtering when\nmore complex logic is required.\n","properties":{"version":{"type":"string","description":"Version identifier for the expression format. Currently only version \"1\" is supported.\n\nThis field ensures future compatibility if the expression format evolves. Always set to \"1\"\nfor current implementations.\n","enum":["1"]},"rules":{"type":"array","description":"Expression array defining filter conditions using prefix notation. The first element is the operator,\nfollowed by its operands which may themselves be nested expression arrays.\n\nThe rule expression follows this pattern:\n- First element: Operator name (string)\n- Remaining elements: Operands for that operator (values or nested expressions)\n\n**Expression structure**\n\nFilter expressions use a prefix notation where operators appear before their operands:\n```\n[operator, operand1, operand2, ...]\n```\n\n**Comparison Operators**\n- `\"equals\"`: Exact match (equals)\n- `\"notequals\"`: Not equal to value (not equals)\n- `\"greaterthan\"`: Value is greater than specified value (is greater than)\n- `\"greaterthanequals\"`: Value is greater than or equal to specified value (is greater than or equals)\n- `\"lessthan\"`: Value is less than specified value (is less than)\n- `\"lessthanequals\"`: Value is less than or equal to specified value (is less than or equals)\n- `\"startswith\"`: String starts with specified prefix (starts with)\n- `\"notstartswith\"`: String does not start with specified prefix (does not start with)\n- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"notendswith\"`: String does not end with specified suffix (does not end with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"empty\"`: Field is missing or empty (is empty)\n- `\"notempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches a regular expression, ignoring case (matches)\n\nOperator names are case-insensitive: `NOTEMPTY` and `endsWith` work and are stored\nas sent. `isempty`, `isnotempty`, `anyof`, `notcontains`, `doesnotstartwith`,\n`doesnotendwith`, `notmatches`, `doesnotmatch` and `notequal` are not operators:\nthey are refused with 422 \"Invalid operation: <name>\", with the name in lowercase,\nboth when the filter is saved and by `POST /v1/processors/filter`. To negate a\ncondition, wrap it in `not`.\n\nThe string comparisons (`equals`, `contains`, `startswith`, `endswith` and their\nnegations) are case-sensitive; wrap the field in `lowercase` to compare without case.\n`matches` is not: every pattern is compiled with the flags `gi`, so `^paid$` also\nmatches `PAID` and `[a-z]` matches capital letters. An inline flag such as `(?i)`\nmakes the pattern invalid. The filter still saves, but in a run every record fails\nwith `FILTER_EVALUATION_FAILED` (\"Invalid regular expression: /(?i)…/gi: Invalid\ngroup\"), and `POST /v1/processors/filter` refuses the call with 422 (live-verified\n2026-10-22).\n\n**Logical Operators**\n- `\"and\"`: All conditions must be true\n- `\"or\"`: At least one condition must be true\n- `\"not\"`: Negates the condition\n\n**Field Access and Type Conversion**\n- `\"extract\"`: Access a field from the item by name\n- `\"settings\"`: Access a custom setting from the flow, flow step, or integration configuration\n- `\"boolean\"`: Convert value to Boolean type\n- `\"epochtime\"`: Convert value to Epoch Time (Unix timestamp)\n- `\"number\"`: Convert value to Number type\n- `\"string\"`: Convert value to String type\n\n**Field Access Details**\n\n**Using `extract` to access record fields:**\n- Retrieves values from the current record being processed\n- Can access nested properties using dot notation (e.g., `\"customer.email\"`)\n- Paths are relative to the record itself: `[\"extract\", \"record.status\"]`\n  matches nothing\n- In a record filter, must be wrapped in a type conversion (`string`, `number`,\n  `boolean` or `epochtime`): a bare `[\"extract\", ...]` operand is refused with 422\n  \"Field operation extract must be called by a Type Conversion Operation but was called\n  by <operator>\", both when an import's filter is saved and by\n  `POST /v1/processors/filter`\n\n**Using `settings` to access configuration values:**\n- Retrieves values from the integration's configuration settings\n- Supports different scopes with prefix notation:\n  - `flow.settingName`: Access flow-level settings\n  - `flowGrouping.settingName`: Access the flow group's settings\n  - `export.settingName`: Access the export's (or lookup's) own settings\n  - `import.settingName`: Access the import's own settings\n  - `integration.settingName`: Access integration-level settings\n  - `connection.settingName`, `iClient.settingName`: Access the step's\n    connection and iClient settings\n- A scope whose resource has no settings can be absent; a rule that reads\n  it matches nothing\n- Must be wrapped in a type conversion like `extract`: a bare\n  `[\"settings\", ...]` operand is refused with 422 \"Field operation settings\n  must be called by a Type Conversion Operation\"\n- Useful for dynamic filtering based on configuration\n\n**Field Transformations**\n- `\"lowercase\"`: Convert string to lowercase\n- `\"uppercase\"`: Convert string to uppercase\n- `\"ceiling\"`: Round number up to the nearest integer\n- `\"floor\"`: Round number down to the nearest integer\n- `\"abs\"`: Get absolute value of a number\n\n**Arithmetic** (number operands)\n- `\"add\"`, `\"subtract\"`, `\"multiply\"`, `\"divide\"`, `\"modulo\"`: for example\n  `[\"subtract\", [\"number\", [\"extract\", \"total\"]], [\"number\", [\"extract\", \"discount\"]]]`.\n  `mod` is not an operator (422 \"Invalid operation: mod.\").\n\n**Dates**\n- `epochtime` reads ISO 8601 strings and epoch-millisecond numbers. An empty\n  string, a string that isn't a date, or epoch milliseconds sent as a string\n  fail it with \"Invalid date. Cannot convert given string or number to date\n  time.\", followed by `; record=` and the record as JSON: in a run that record fails (`FILTER_EVALUATION_FAILED`, source\n  `output_filter` on an export), and `POST /v1/processors/filter` and an\n  export preview refuse the whole call with 422. `and` and `or` evaluate\n  every operand, so a `notempty` test beside it doesn't prevent the failure.\n  A missing or null field doesn't match.\n- `datetime`, `date` and `timestamp` are not type conversions: 422\n  \"Invalid operation: <name>.\"\n- `number` reads a missing or empty field as 0.\n\n**Handlebars operands**\n- An operand can also be a string holding exactly one Handlebars expression, such as\n  `\"{{record.status}}\"` or `\"{{lowercase record.code}}\"`, on either side of the\n  operator. It renders against the record (`record.<field>`; an unprefixed\n  `{{status}}` renders empty) before the comparison.\n- The rendered value is a string: wrap it in `[\"number\", ...]` to compare it with a\n  number, or the rule is refused with 422.\n- `epochtime` does not render Handlebars: `[\"epochtime\", \"{{timestamp}}\"]` is refused\n  with 422 \"invalid datetime format\".\n- A string with text around the expression, or with more than one expression, is\n  compared as a literal.\n- `[\"number\", \"{{timestamp \\\"x\\\"}}\"]` is the current time in epoch milliseconds,\n  so `[\"greaterthanequals\", [\"epochtime\", [\"extract\", \"created\"]], [\"subtract\",\n  [\"number\", \"{{timestamp \\\"x\\\"}}\"], 604800000]]` keeps records created in the\n  last 7 days.\n- An unknown helper fails the rule with 422 \"Failed to evaluate handlebars\n  expression\".\n\nChoose the type conversion that matches the comparison — `number` for numeric\ncomparisons, `epochtime` for dates, `string` for text. For example:\n\n```json\n[\n  \"equals\",\n  [\n    \"number\",  // Convert to number before comparison\n    [\n      \"extract\",\n      \"quantity\"\n    ]\n  ],\n  100\n]\n```\n\nExample with datetime conversion:\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",  // Convert to Unix timestamp before comparison\n    [\n      \"extract\",\n      \"createdDate\"\n    ]\n  ],\n  1609459200000  // January 1, 2021 as Unix timestamp in milliseconds\n]\n```\n\nExample with transformations:\n```json\n[\n  \"and\",\n  [\n    \"matches\",\n    [\n      \"lowercase\",  // Convert to lowercase before matching\n      [\n        \"string\",\n        [\n          \"extract\",\n          \"categories\"\n        ]\n      ]\n    ],\n    \"netsuite\"\n  ],\n  [\n    \"notequals\",\n    [\n      \"string\",\n      [\n        \"extract\",\n        \"recurrence.pattern.type\"\n      ]\n    ],\n    \"\"\n  ]\n]\n```\n\nExample comparing a record field with a flow setting:\n```json\n[\n  \"equals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"trantype\"\n    ]\n  ],\n  [\n    \"string\",\n    [\n      \"settings\",\n      \"flow.trantype\"\n    ]\n  ]\n]\n```\n\n**Examples**\n\nExample 1: Status field is not equal to \"cancelled\"\n```json\n[\n  \"notequals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"status\"\n    ]\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"filename\"\n    ]\n  ],\n  \"HC\"\n]\n```\n\nExample 3: Amount is greater than 100\n```json\n[\n  \"greaterthan\",\n  [\n    \"number\",\n    [\n      \"extract\",\n      \"amount\"\n    ]\n  ],\n  100\n]\n```\n\nExample 4: Order date is after January 1, 2023 (UTC). Compare dates as `epochtime`\nagainst epoch milliseconds: a `string` comparison of ISO timestamps orders values with\ndifferent UTC offsets wrongly.\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",\n    [\n      \"extract\",\n      \"orderDate\"\n    ]\n  ],\n  1672531200000\n]\n```\n","items":{"oneOf":[{"title":"String","type":"string"},{"title":"Number","type":"number"},{"title":"Boolean","type":"boolean"},{"title":"Object","type":"object"},{"title":"Array","type":"array"},{"title":"Null","type":"null"}]}}}},"script":{"type":"object","description":"Configuration for programmable script-based filtering. This object enables complex, custom\nfiltering logic beyond what expression-based filtering can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to determine which items\nshould be processed.\n\n**Implementation approach**\n\nScript-based filtering works by:\n1. Executing the specified function from the referenced script\n2. Passing item data to the function\n3. Using the function's return value (true/false) to determine inclusion\n\n**Common use cases**\n\nScript filtering is ideal for:\n- Complex business logic that can't be expressed as simple rules\n- Dynamic filtering criteria that change based on external factors\n- Content-based filtering that requires deep inspection\n- Advanced pattern matching beyond simple wildcards\n- Multi-stage filtering with intermediate logic\n\nFor AI agents: Only use script-based filtering when expression-based filtering is insufficient.\nScript filtering requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to the Script resource that contains the filtering logic. This must be a valid\nObjectId of a Script resource in this account: any other id fails the save with 422\n(\"Only a script owned by the user or a script shared by the Integration App can be used\nat filter.script._scriptId.\").\n\nThe referenced script must contain the function specified in the `function` field\nand must be written to handle filtering specifically. The script receives\nitem data as its input and must return a boolean value indicating whether\nto process the item (true) or skip it (false).\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n"},"function":{"type":"string","description":"Name of the function within the script to execute for filtering decisions. This function\nmust exist in the script referenced by _scriptId.\n\n**Function requirements**\n\nThe specified function must:\n- Accept item data as its first parameter\n- Return a boolean value (true to process the item, false to skip it)\n- Handle errors gracefully\n- Execute efficiently (as it may run for many items)\n\n**Function signature**\n\n```javascript\nfunction filterItems(itemData) {\n  // itemData contains properties of the item being evaluated\n  // Custom logic here\n  return true; // or false to skip the item\n}\n```\n\nFor AI agents: Ensure the function name exactly matches a function defined in the\nreferenced script, as mismatches will cause the filter to fail.\n"}}}}},"Distributed":{"type":"object","description":"Authentication settings for distributed (real-time listener) exports.","properties":{"bearerToken":{"type":"string","description":"Bearer token that a system pushing data into this export sends as\n`Authorization: Bearer <token>`. Optional — most distributed listeners (NetSuite,\nSalesforce real-time) use no token. Write-only and stored encrypted; treat it as a\nsensitive credential.","format":"password"}}},"Cdc":{"type":"object","description":"Change-data-capture configuration for real-time listener exports (the \"Listen for real-time\ndata\" source step). Present only when `type` is `stream`. Set on `MongodbExport` (MongoDB change\nstreams) and `RDBMSExport` (SQL Server CDC and PostgreSQL logical replication); the listener\nstreams change events continuously instead of fetching records on a schedule.\n\nOn save the API normalizes the `properties` grid into the typed fields: a `snapshot.mode`\nentry populates `snapshotMode` and a `capture.mode` entry populates `captureMode`. Every\nstream export must resolve a snapshot mode from one of those two places.","properties":{"captureMode":{"type":"string","enum":["change_streams_update_full","change_streams_update_full_with_pre_image"],"default":"change_streams_update_full","description":"How MongoDB change-stream events are materialized into records. Use\n`change_streams_update_full_with_pre_image` when downstream steps need the document's prior\nstate. MongoDB listeners only — `RDBMSExport` (SQL Server CDC, PostgreSQL) does not set this."},"snapshotMode":{"type":"string","enum":["initial","no_data","when_needed","initial_only"],"default":"no_data","description":"Whether the listener seeds existing rows before streaming changes. Use `initial` to backfill\nexisting data on the first run; `no_data` to stream only new changes."},"slotName":{"type":"string","pattern":"^[a-z_][a-z0-9_]{0,62}$","description":"Name of the PostgreSQL logical replication slot the listener consumes. PostgreSQL listeners\nonly. When omitted, the platform manages the slot name. Must be a valid PostgreSQL\nidentifier (lowercase letters, numbers, or underscores, starting with a letter or\nunderscore)."},"publicationName":{"type":"string","pattern":"^[a-z_][a-z0-9_]{0,62}$","description":"Name of the PostgreSQL publication that defines which tables stream changes. PostgreSQL\nlisteners only. When set, the publication is the sole table filter and `rdbms.tables`\nstays unset. Must be a valid PostgreSQL identifier."},"cursor":{"type":"string","description":"Server-managed stream position marker (where the listener resumes reading change events).\nMaintained by the platform as events are consumed; the UI exposes a reset workflow for it\nunder Cursor Management."},"paths":{"type":"array","items":{"type":"string"},"description":"Dot-notation paths within each raw change event to retain in the emitted record. MongoDB\nlisteners default to `[\"payload\"]` (the whole change document); SQL Server and PostgreSQL\nlisteners commonly select `payload.after`, `payload.op`, and related fields to expose the\nchanged row and operation type."},"intervalTime":{"type":"integer","default":300,"description":"Polling interval, in seconds, at which the listener checks the source for new change events."},"properties":{"type":"array","description":"Connector-engine settings passed through to the underlying CDC connector as name/value\npairs. Mirrors the form's additional-properties grid: the required keys (`capture.mode`\nand `snapshot.mode` for MongoDB, `snapshot.mode` for SQL Server and PostgreSQL) are\nserialized first, followed by optional keys such as `field.exclude.list`,\n`column.include.list`, or `snapshot.collection.filter.overrides.<db>.<collection>`.\nOn save the API also lifts `snapshot.mode` / `capture.mode` entries into the typed\n`snapshotMode` / `captureMode` fields.","items":{"type":"object","required":["name","value"],"properties":{"name":{"type":"string","description":"Connector property key (e.g. `capture.mode`, `snapshot.mode`, `field.exclude.list`)."},"value":{"type":"string","description":"Connector property value."}}}}}},"Topic":{"type":"object","description":"Subscription settings for an Event Streams listener (`adaptorType: TopicExport`). The\nlistener consumes messages published to the topic and hands them to the flow in batches;\nthe platform delivers them internally, so the export carries no `_connectionId`. Present\nwhen `adaptorType` is `TopicExport`; the server fills `startPosition` and `maxWaitTimeMs`\nwith their defaults when they are omitted.","properties":{"_topicId":{"type":"string","format":"objectId","description":"Topic this listener subscribes to. Create or look it up with `GET /v1/topics`; the\ntopic must belong to the same account. Pointing an existing listener at a different\ntopic starts a new subscription, read from `startPosition`. A clone made in the same\nenvironment — of this export, or of a flow that contains it — keeps `_topicId`, so the\ncopy becomes another listener on the topic once its flow is enabled."},"startPosition":{"type":"string","enum":["latest","earliest"],"default":"latest","description":"Where the listener starts reading when it first subscribes to `_topicId`. Use `earliest`\nto backfill retained history, `latest` (the default) to process only new messages. Read\non create and whenever `_topicId` changes; on other updates the stored value is kept even\nif a different value is sent."},"maxWaitTimeMs":{"type":"integer","minimum":1000,"default":30000,"description":"Longest time, in milliseconds, the listener waits to fill a batch before delivering\nwhatever it has collected. Lower values reduce latency for sparse topics; the default\nof 30 seconds favors fuller batches. The batch record cap is the export's `pageSize`."}}},"FileSystem":{"type":"object","description":"Defines which files to read from a local or mounted folder on the host running the\non-premise agent. Required when the _connectionId field references a file-system (on-premise)\nconnection; must not be included for other connection types.","required":["directoryPath"],"properties":{"directoryPath":{"type":"string","description":"Folder on the on-premise agent's host to read files from; the agent's OS account must\nhave read permission on it. Accepts a local OS path or a UNC network share, and supports\nhandlebars templates for dynamic folders."}}},"Http":{"type":"object","description":"Configuration for HTTP exports. Required when the export's `_connectionId` references a\nconnection of type `http`, and on `NetSuiteHTTPExport` (NetSuite's REST APIs on a `netsuite`\nconnection, where `nsDomainType` supplies the host).","properties":{"type":{"type":"string","enum":["file","blob","csvstream"],"description":"Set to `file` when the export works with files rather than a record-returning endpoint: raw file downloads (PDFs, images, binary data) and the folder mode of the cloud file providers (Google Drive, Box, Dropbox, Azure Blob, Google Cloud Storage, Celigo Storage), where each file in the folder is parsed by the `file` object's `type` (csv, json, xml, xlsx, filedefinition — including EDI) into records; the `file` object must also be configured. Leave undefined for standard data exports that return structured records (JSON, XML, GraphQL, SOAP responses) — the response is then parsed into records for downstream steps. When set to `file` or `blob`, the export appears as a \"Transfer\" step in Flow Builder instead of a standard \"Export\" step."},"formType":{"type":"string","enum":["http","rest","graph_ql","assistant","assistant_graphql"],"description":"Authoring mode of the HTTP request form. Controls which editor the UI presents\n(standard HTTP, legacy REST, GraphQL, or connector-assistant driven) and how the\nrequest fields are interpreted. When omitted, the standard HTTP form is used."},"method":{"type":"string","description":"HTTP method used to request data from the target API. Consult the target API's\ndocumentation to determine the appropriate method. `HEAD` exists for connector endpoints\nthat return metadata in response headers only (Azure Data Lake Storage Gen2 path\nproperties).","enum":["GET","POST","PUT","PATCH","DELETE","HEAD"]},"followRedirects":{"type":"boolean","default":true,"description":"When explicitly false, 3xx responses are not followed — the redirect\nresponse itself (status code, `Location` header, body) becomes the\nrecord. Omitted or true follows allowed redirects (the default\nbehavior)."},"maxRedirects":{"type":"integer","minimum":1,"maximum":10,"description":"Caps how many consecutive 3xx redirects are followed. Only applies\nwhen `followRedirects` is not false; to not follow at all, set\n`followRedirects: false` rather than `maxRedirects: 0`. Decimal\nvalues are rejected on save."},"relativeURI":{"type":"string","description":"Resource path appended to the connection's `baseURI` to form the complete request URL. Path segments, query parameters, or the entire value can be built with handlebars expressions for endpoints determined at runtime. For lookup exports (`isLookup: true`) with mappings configured, handlebars always render against the original pre-mapped input record, so fields removed or renamed by mappings remain available for URI construction. On `NetSuiteHTTPExport` the base URL is derived from the NetSuite connection and `nsDomainType`, so the path starts at `/services/rest/...` or `/app/site/hosting/restlet.nl`.\n\nLeave it empty to call the connection's `baseURI` exactly; `/` appends a trailing slash, which is a different URL. An export saved with an empty relative URI and no `http.body` comes back from reads without its `http` block, so a PUT built from such a read loses the stored settings: send the complete `http` block with every PUT."},"headers":{"type":"array","description":"Headers specific to this export, merged with (and able to override) headers defined on the connection. Define common headers such as authentication on the connection instead. Values support handlebars expressions; for lookup exports with mappings, values render against the pre-mapped input record.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header to send; a header defined here overrides a same-named header from the connection."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions; for lookup exports with mappings, the value renders against the pre-mapped input record."}}}},"requestMediaType":{"type":"string","description":"Overrides the connection-level request media type for this export. Set only when this endpoint requires a different format than the connection default.","enum":["json","xml","urlencoded","form-data","plaintext"]},"body":{"type":"string","description":"Request body sent with POST, PUT, or PATCH requests, typically carrying query or filter criteria for APIs (such as GraphQL or SOAP) that expect them in the body. The content must match the format set by `requestMediaType` and supports handlebars expressions for dynamic values."},"successMediaType":{"type":"string","description":"Media type used to parse successful response bodies. Set only when the response format differs from the request format.","enum":["json","xml","csv","plaintext"]},"errorMediaType":{"type":"string","description":"Media type used to parse error response bodies. Set only when error responses use a different format than the request.","enum":["json","xml","plaintext"]},"_asyncHelperId":{"type":"string","format":"objectId","description":"AsyncHelper resource that handles polling for long-running operations on APIs that process requests asynchronously (HTTP 202 responses, job tickets, feed or document IDs). Set when the export must submit a request, poll for status, and retrieve results once the external process completes — for example Amazon SP-API feeds or large report generators."},"once":{"type":"object","description":"Callback configuration for once exports, used to mark records as exported in the source system after successful processing.\nCalled only while the export's `type` is `once`: an export with no `type` keeps a saved `once` and never calls it, so every\nrun reads every record again (live-verified 2026-10-22).","properties":{"relativeURI":{"type":"string","description":"Relative path (starting with `/`) called on the source system to mark each record as exported after successful processing. Supports handlebars variables and renders against the pre-mapped record — mappings do not apply to the callback URI."},"method":{"type":"string","description":"HTTP method used for the mark-as-exported callback request.","enum":["GET","PUT","POST","PATCH","DELETE"]},"body":{"type":"string","description":"Request body sent with the mark-as-exported callback. Supports handlebars expressions for dynamic values."}}},"paging":{"type":"object","description":"Controls how the export requests subsequent pages when the API returns multi-page responses. The `method` field determines which companion fields are required and how each next page is requested. For the page, skip, and token methods, reference the matching pagination variable (`{{export.http.paging.page}}`, `{{export.http.paging.skip}}`, or `{{export.http.paging.token}}`) in the relative URI or request body — misconfigured pagination is a common cause of incomplete data retrieval.\n\nWhen this export is the first lookup of a builder-mode API with `pagination.enabled` set, it is that API's volume driver: saving the export (even unchanged) invalidates the API's issued cursors, so a caller mid-walk gets an empty final page, and removing `paging` is accepted on save but makes every call to the API fail with `400 api_pagination_source_not_paged` until paging is restored or the API's flag is turned off.\n\nWhen a next page's response is identical to the previous one, paging stops: the export records an open error with code `page_response_error` (\"Identical response received from next page request. No more page requests will be made.\") and the run completes with the pages already read, so later pages are never exported. A request that doesn't change between pages produces this, as observed with `method: page` and the pagination variable missing from the relative URI and body; reference the variable so each request asks for the next page.","properties":{"method":{"type":"string","description":"Pagination strategy used to request each subsequent page; match it to the mechanism documented by the target API. Determines which companion fields are required: `token` needs `path` and `pathLocation`, `url` needs `path`, `relativeuri` needs `relativeURI`, `body` needs `body`, while `linkheader` typically needs no extra configuration. Using the wrong method results in errors or incomplete data retrieval.","enum":["linkheader","page","skip","token","url","relativeuri","body"]},"page":{"type":"integer","description":"Starting page number for `method: page`. Set to 1 for APIs whose first page is not zero-indexed; when omitted, paging starts at 0. The value is incremented automatically for each subsequent page request."},"skip":{"type":"integer","description":"Starting offset for `method: skip`. Rarely needed — most APIs start at 0, and the value is incremented by the page size automatically for each subsequent request. Set only when the API requires a non-zero starting offset."},"token":{"type":"string","description":"Initial token for `method: token`. Leave empty for normal pagination — the first request is sent without a token, and subsequent tokens are extracted from each response via `path`. Set only to resume from a known token or for APIs that require a token on the first request."},"path":{"type":"string","description":"Location of the pagination value in each response — the continuation token for `method: token`, or the complete next-page URL for `method: url`. When `pathLocation` is `body`, use a dot-notation JSON path (e.g. `meta.nextToken`); when `header`, use the exact case-sensitive header name. Not used by other pagination methods."},"pathLocation":{"type":"string","description":"Where the export looks for the pagination token referenced by `path`. Required for `method: token`; not used by other pagination methods.","enum":["body","header"]},"pathAfterFirstRequest":{"type":"string","description":"Alternative token location used for responses after the first page, in the same format as `path`. Set only when the API moves the token to a different location in subsequent responses — setting it unnecessarily can cause pagination to fail."},"relativeURI":{"type":"string","description":"Overrides the main relative URI for second and later page requests; leave empty when the main relative URI works for all pages. Build it with handlebars using the `previous_page` context — `previous_page.full_response` (the prior response body), `previous_page.last_record` (the last record of the prior page), and `previous_page.headers` (the prior response headers)."},"body":{"type":"string","description":"Overrides the main request body for second and later page requests, typically for GraphQL or SOAP APIs that paginate through the body; leave empty when the main body works for all pages. Build it with handlebars using the `previous_page` context — `previous_page.full_response`, `previous_page.last_record`, and `previous_page.headers`."},"mergeBodyParamsToPagingBody":{"type":"boolean","description":"Only applies when `paging.method` is `body`. When true, the body\nparameters produced by the export's mappings at runtime are merged\ninto the evaluated `paging.body` for page 2+ requests, so paging\nrequests keep the same mapped parameters as the first page.\nOmitted/false keeps the original behavior — page 2+ requests send\nonly the evaluated `paging.body`."},"linkHeaderRelation":{"type":"string","description":"Link header relation followed for `method: linkheader` when the API uses a value other than the default `next`. Case-sensitive and must exactly match the `rel` value in the Link header, without the `rel=` prefix."},"resourcePath":{"type":"string","description":"Overrides the path to records for second and later page responses. Set only when follow-up pages place records at a different location than the first response; leave empty when all pages share the same structure."},"lastPageStatusCode":{"type":"integer","description":"HTTP status code that signals the last page, replacing the default behavior of treating 404 as the end of pagination. When this status is received (any code but `200` paired with `lastPagePath`), paging stops and the response is not treated as an error. Set it when the API uses a non-404 code (such as 204 or 400) to indicate no more pages, or to `200` together with `lastPagePath` when the last page is an ordinary 200 response: then the response is parsed, its records are kept, and paging stops only when `lastPagePath` matches `lastPageValues`."},"lastPagePath":{"type":"string","description":"JSON path to a response-body field that signals the end of pagination, such as a \"has more\" flag or a cursor that empties on the last page. Must be used with `lastPageValues`, which lists the values at this path that stop paging. If the path does not exist in a response, the condition is not considered met. With `method: token` the path is read only on a response whose status code equals `lastPageStatusCode`, so pair it with `lastPageStatusCode: 200` for an ordinary last page; with `method: body` it is read on every response."},"lastPageValues":{"type":"array","description":"Values at `lastPagePath` that stop pagination; a match on any entry ends paging. All entries are compared as exact, case-sensitive strings, even for boolean or numeric fields — use `\"true\"`, `\"false\"`, `\"null\"` for JSON null, or `\"\"` for an empty string. Required when `lastPagePath` is set.","items":{"type":"string"}},"maxPagePath":{"type":"string","description":"JSON path to the total page count in the response, used to stop paging once the last page is reached. Only applies to the `page` and `skip` methods. Point it at the total number of pages, not the current page number."},"maxCountPath":{"type":"string","description":"JSON path to the total record count in the response, used to stop paging once all records have been retrieved. Only applies to the `page` and `skip` methods; when both are set, `maxPagePath` takes precedence. Point it at the total number of records, not the count in the current page."}},"if":{"properties":{"method":{"const":"body"}},"required":["method"]},"then":{"required":["body"]},"else":{"if":{"properties":{"method":{"const":"token"}},"required":["method"]},"then":{"required":["path","pathLocation"]},"else":{"if":{"properties":{"method":{"const":"relativeuri"}},"required":["method"]},"then":{"required":["relativeURI"]},"else":{"if":{"properties":{"method":{"const":"url"}},"required":["method"]},"then":{"required":["path"]}}}}},"response":{"type":"object","description":"Controls how records are extracted from the API response and how success or failure is detected at the response level. When the API wraps records in an envelope object, set `resourcePath` to the path of the records array — without it, the entire response body is treated as a single record. Leave this object undefined when the API returns a bare JSON array.","properties":{"resourcePath":{"type":"string","description":"Dot-separated path to the array of records inside the response body (e.g. `data.customers` for `{\"data\": {\"customers\": [...]}}`); without it, a wrapped response is treated as a single record. Values starting with `$` are evaluated as JSONPath instead of dot notation — including unions (`$['orders','invoices'][*]`) and filter expressions (`$.items[?(@.qty>3)]`); all other values keep the existing dot-notation behavior unchanged. Leave undefined when the API returns a bare JSON array. This extracts records from the API response — not to be confused with `oneToMany`/`pathToMany`, which unwrap arrays from input records, or `paging.resourcePath`, which applies only to subsequent page responses."},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"},"resourceIdPath":{"type":"string","description":"Path to the unique identifier within each record, used primarily when processing results of asynchronous import responses. When omitted, the system looks for standard `id` or `_id` fields automatically."},"successPath":{"type":"string","description":"Path to a response field that signals whether the call succeeded, for APIs that return HTTP 200 even on failure. With `successValues`, the value at this path must match one of them; without `successValues`, the path only has to resolve — a response in which it is absent fails with `response_failure` (\"Unable to extract paths from response body using path: <path>, Response: …\")."},"successValues":{"type":"array","items":{"type":"string"},"description":"Values at `successPath` that mark the response as successful; any other value is treated as an error. All comparisons are string-based — use `\"true\"` or `\"false\"` for boolean fields."},"errorPath":{"type":"string","description":"Path to the error message in the response body. The value at this path is included in error logs and error records when the API returns an error; without it, the message is the whole response body."},"failPath":{"type":"string","description":"Path to a response field that signals failure even when the HTTP status code is 200 — the inverse of `successPath`. With `failValues`, the response fails when the value there matches one of them; without, when the path holds a truthy value. A path that resolves to an array matches only when the array has exactly one element."},"failValues":{"type":"array","items":{"type":"string"},"description":"Values at `failPath` that mark the response as failed, even when the HTTP status code is 200."},"allowArrayforSuccessPath":{"type":"boolean","description":"When true, treats the value at `successPath` as an array and counts the response as successful if any element matches `successValues`, rather than requiring a single scalar match. Set it for APIs that return per-record status arrays in a batch response."},"twoDArray":{"type":"object","description":"Parsing options for endpoints that return tabular data as a two-dimensional array (rows of cells) rather than an array of objects, such as spreadsheet-style or report APIs.","properties":{"hasHeader":{"type":"boolean","description":"When true, the first row is treated as column headers and used to name the fields of each generated record."},"doNotNormalize":{"type":"boolean","description":"When true, the rows are passed through as raw arrays instead of being normalized into keyed records."}}},"blobFormat":{"type":"string","description":"Controls how the binary response body is decoded for blob exports. Only relevant when `http.type` is `file` or the export type is `blob`.","enum":["utf8","ucs2","utf-16le","ascii","binary","base64","hex"]}}},"_httpConnectorVersionId":{"type":"string","format":"objectId","description":"Pins the export to a version of the published HTTP connector its connection is bound to\n(`connection.http._httpConnectorId`), so the UI renders that connector's endpoint form instead\nof a hand-authored request. Linked on save together with `_httpConnectorResourceId` and\n`_httpConnectorEndpointId` — see that field for the rule."},"_httpConnectorResourceId":{"type":"string","format":"objectId","description":"The connector resource (a group of related endpoints, such as Orders) that this export's\nendpoint belongs to. Linked on save together with `_httpConnectorEndpointId` — see that field\nfor the rule."},"sendAuthForFileDownloads":{"type":"boolean","description":"When true, includes authentication headers when downloading files."},"_httpConnectorEndpointId":{"type":"string","format":"objectId","description":"The connector endpoint this export calls, shown in the UI as the selected endpoint. On save the\nplatform derives it (with the version and resource ids) from `relativeURI` and `method` when\nthey match an endpoint of the connector the connection is bound to — path parameters, literal\nids and query strings still match — and replaces ids that belong to another connector. An id for\nthe connection's own connector is kept as sent even when the URI names a different endpoint;\nwhen nothing matches, or the connection has no connector, whatever was sent is kept and nothing\nis derived."}}},"Salesforce":{"type":"object","description":"Configuration for Salesforce exports. Required when the export's `_connectionId` references a Salesforce connection; omit for other connection types. The `type` field selects the extraction mode: `soql` runs batch queries and requires the `soql` object, while `distributed` listens for real-time events and requires the `distributed` object. File retrieval (when the export's `type` is `blob`) requires `sObjectType` and `id` instead.","properties":{"type":{"type":"string","description":"Selects the extraction mode and determines which configuration object is required. Use `soql` for scheduled or on-demand batch queries and lookups — it requires the `soql` object and works with both the `rest` and `bulk` APIs and the standard, delta, test, and once export types. Use `distributed` for real-time event handling — it requires the `distributed` object, always appears as a Listener in Flow Builder, ignores the `api` field, and supports only the standard export type.","enum":["soql","distributed"]},"sObjectType":{"type":"string","description":"API name of the Salesforce object the export operates on; the object must exist in the connected org and be accessible to the integration user. Required for distributed exports and for blob exports — blob exports accept only the file storage objects (`Attachment`, `ContentVersion`, `Document`) paired with the `id` field. Optional for SOQL exports, where the object can be inferred from the query."},"id":{"type":"string","description":"Salesforce record ID of the file to retrieve; required for blob exports and not used for `soql` or `distributed` exports. Accepts a static ID or a handlebars expression such as `{{record.Attachment_Id__c}}` to resolve the file at runtime — dynamic IDs require the export to run as a lookup (`isLookup: true`). The ID must belong to the object named in `sObjectType`."},"includeDeletedRecords":{"type":"boolean","description":"When true, SOQL exports use Salesforce's `queryAll()` instead of `query()`, including Recycle Bin records deleted within the past 15 days; each record's `IsDeleted` field identifies deletions. Useful for synchronizing deletes to target systems or maintaining a complete audit trail. Ignored for distributed and blob exports.","default":false},"api":{"type":"string","description":"Salesforce API used to run SOQL queries; ignored for distributed and blob exports. Use `rest` (the default when omitted) for smaller datasets (under 10,000 records) and for lookup exports — the Bulk API is not compatible with `isLookup: true`. Use `bulk` for large data volumes and higher throughput, optionally tuned through the `bulk` object.","enum":["rest","bulk"]},"bulk":{"type":"object","description":"Settings for Salesforce Bulk API 2.0 jobs. Only applies when `api` is `bulk` on a SOQL export; ignored otherwise.","properties":{"maxRecords":{"type":"integer","description":"Caps how many records a single Bulk API job retrieves, which helps prevent timeouts with complex queries or large records. When omitted, Salesforce's default applies. Lower values suit complex or custom objects; higher values improve throughput for simple records.","minimum":10000},"purgeJobAfterExport":{"type":"boolean","default":true,"description":"When true, deletes the Bulk API job in Salesforce after all data is retrieved, keeping the Bulk Data Load Jobs list clean — at the cost of making job details unavailable for later troubleshooting. Has no effect on the data retrieved or the success of the export."}}},"soql":{"type":"object","description":"SOQL query configuration; required when `type` is `soql` and not used otherwise. The query controls which objects, fields, and filter conditions the export retrieves, with either the REST or Bulk API.\n\nRequired when type is soql.","properties":{"query":{"type":"string","description":"SOQL statement passed directly to Salesforce that defines which objects, fields, and records the export retrieves. Supports handlebars — for delta exports, filter with `WHERE LastModifiedDate > {{lastExportDateTime}}` — and relationship queries that fetch parent and child records together. Select only the fields you need and use `ORDER BY` for consistent results across pages.","maxLength":200000}}},"distributed":{"type":"object","description":"Real-time listener configuration — the object that makes an export a listener. Set it, with `type: distributed`, when creating one; a listener saved from the integrator.io form carries it with no `type`, and the platform fires it either way. Not used on SOQL or file exports. Records reach the flow only when an Apex trigger on `sObjectType` in the connected org calls the Celigo package (`integrator_da__.RealTimeExporter.processExport()`) — no scheduling or manual execution is involved. Integration Apps create their triggers at installation; for a custom flow the customer deploys the trigger in Salesforce, starting from the code the export page shows under Required trigger.","properties":{"referencedFields":{"type":["array","null"],"description":"Fields from related objects to include in the exported record, written in dot notation (e.g. `Account.Name`, `Owner.Email`, `Custom_Lookup__r.Field__c`). Works with lookup and master-detail relationships, up to 10 unique referenced relationships per export. Referenced fields are retrieved via separate API calls, so include only fields the integration actually needs.","items":{"type":["string","null"]}},"disabled":{"type":"boolean","description":"When true, the listener stops processing events while its configuration and Salesforce triggers stay in place. Events that occur while disabled are ignored, not queued — they are not processed retroactively when re-enabled, so consider a delta export to catch up after extended pauses."},"qualifier":{"type":["string","null"],"description":"SOQL WHERE-clause condition that filters which record events the listener processes; when omitted (stored as `null`), every event for the object is processed. The Celigo Salesforce package evaluates it inside Salesforce as `SELECT Id FROM <sObjectType> WHERE (<qualifier>) AND Id IN :ids` before anything is sent, so only fields of the monitored object and SOQL comparison syntax apply — Salesforce formula functions are not part of this grammar. A deleted record is never returned by that query, so a listener with any qualifier receives no delete events; leave it unset when removals must trigger the flow."},"batchSize":{"type":["integer","null"],"description":"Controls how many event records are grouped into each real-time processing batch. Stored as `null` when not configured. Smaller batches lower latency for time-sensitive operations; larger batches improve throughput for high-volume objects. Does not limit how many records are processed in total — only how they are grouped.","minimum":4,"maximum":200},"skipExportFieldId":{"type":["string","null"],"description":"API name of a Salesforce checkbox field used to prevent infinite loops in bidirectional syncs: when the integration updates a record, this field is set so the resulting event is ignored, then cleared automatically. Required for bidirectional sync scenarios. The field must be a checkbox dedicated to integration use and updateable by the integration user."},"relatedLists":{"type":["array","null"],"description":"Child record sets to include with the parent record when it changes, one entry per related list. When omitted (stored as `null`), only the primary record is processed. Each related list adds Salesforce API calls and increases payload size.","items":{"type":"object","description":"Defines how to retrieve one type of child record related to the primary object. Configure multiple entries to retrieve different types of children.","properties":{"referencedFields":{"type":"array","description":"API names of the child object fields to retrieve; only the listed fields are included, and an empty array retrieves only the `Id` field. Include only fields the integration needs — each field adds data volume and processing time.","items":{"type":["string","null"]}},"parentField":{"type":"string","description":"API name of the lookup or master-detail field on the child object that references the parent (e.g. `AccountId`, `Parent_Object__c`) — the field's API name, not the relationship name. Used to build the query that fetches children for each parent record."},"sObjectType":{"type":"string","description":"Case-sensitive API name of the child object to retrieve (e.g. `Contact`, `Custom_Child__c`) — use the API name, not the label. The object must have a relationship field to the parent and be accessible to the connected user."},"filter":{"type":"string","description":"SOQL condition that limits which child records are included; when omitted, all related children are retrieved. Provide only the condition expression without the `WHERE` keyword — it is combined automatically with the parent relationship filter."},"orderBy":{"type":"string","description":"Sort order for the retrieved child records, as field names with optional `ASC`/`DESC` directions (e.g. `CreatedDate DESC`). Provide only the fields and directions without the `ORDER BY` keywords. When omitted, Salesforce determines the order."}},"required":["parentField","referencedFields","sObjectType"]}}}}},"if":{"properties":{"type":{"const":"distributed"}},"required":["type"]},"then":{"required":["distributed","sObjectType"]},"else":{"if":{"required":["distributed"]},"then":{},"else":{"if":{"required":["id"]},"then":{"required":["sObjectType"]},"else":{"if":{"not":{"propertyNames":{"enum":["metadata"]}}},"then":{"required":["soql","type"]}}}}},"AS2":{"type":"object","description":"Configures AS2 exports and listeners. Optional — the transport settings live on the AS2\nconnection, and an AS2 export is valid with only `file` (parse settings); set this object\nto link a Trading Partner Connector or to transfer raw files as blobs. An AS2 listener\nacts as the flow's source, receiving trading-partner transmissions in near real-time and\nhandling decryption, signature verification, and MDN generation; AS2 (Applicability\nStatement 2) transmits EDI and other data securely over HTTP/S using S/MIME encryption\nand digital signatures.","properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies the partner-specific EDI configuration —\ncommunication protocol, document schemas, mappings, validation rules, and endpoint\ndetails. Set this to link the export to all settings required for AS2 communication\nwith that partner."},"blob":{"type":"boolean","description":"When true, retrieves raw files without parsing them into structured records (rendered as\na \"Transfer\" step in the flow UI). Use only when the file contents are not needed in\nsubsequent steps — for binary files or when parsing is handled downstream. Only\navailable on AS2 and VAN exports."}},"required":[]},"DynamoDB":{"type":"object","description":"Defines how records are queried from DynamoDB tables. Required when the _connectionId field\nreferences a DynamoDB connection; must not be included for other connection types. Basic\nexports need region, method, tableName, keyConditionExpression, expressionAttributeNames,\nand expressionAttributeValues; once exports (export type \"once\") additionally need\nonceExportPartitionKey, plus onceExportSortKey for composite-key tables.","required":["region","tableName","keyConditionExpression","expressionAttributeNames","expressionAttributeValues"],"properties":{"region":{"type":"string","enum":["us-east-1","us-east-2","us-west-1","us-west-2","af-south-1","ap-east-1","ap-south-1","ap-northeast-1","ap-northeast-2","ap-northeast-3","ap-southeast-1","ap-southeast-2","ca-central-1","eu-central-1","eu-west-1","eu-west-2","eu-west-3","eu-south-1","eu-north-1","me-south-1","sa-east-1"],"description":"AWS region hosting the DynamoDB table. Must match the region where the table is\ndeployed so the integration can reach it.","default":"us-east-1"},"method":{"type":"string","enum":["query"],"description":"DynamoDB operation used to retrieve items. Only \"query\" is currently supported."},"tableName":{"type":"string","description":"Name of the DynamoDB table to query. Must exactly match an existing table (case-sensitive)."},"keyConditionExpression":{"type":"string","description":"Key condition determining which items the query retrieves. Must include a condition on\nthe partition key and may add sort-key conditions (equality, BETWEEN, begins_with).\nReference attribute names with \"#\" placeholders defined in expressionAttributeNames and\nvalues with \":\" placeholders defined in expressionAttributeValues."},"filterExpression":{"type":"string","description":"Filters query results on non-key attributes after the key condition is applied; omit to\nreturn all items matching the key condition. Uses the same \"#\" and \":\" placeholders\ndefined in expressionAttributeNames and expressionAttributeValues."},"projectionExpression":{"type":"array","items":{"type":"string"},"description":"Attributes to return from each item, reducing data transfer; omit to return all\nattributes. Each array element is one field, referenced via \"#\" placeholders defined in\nexpressionAttributeNames."},"expressionAttributeNames":{"type":"string","description":"JSON string mapping \"#\" placeholders to actual attribute names, e.g.\n{\"#pk\": \"customerId\"}. Placeholders defined here are used in keyConditionExpression,\nfilterExpression, and projectionExpression."},"expressionAttributeValues":{"type":"string","description":"JSON string mapping \":\" placeholders to comparison values, e.g. {\":status\": \"ACTIVE\"}.\nValues can be static or dynamic handlebars expressions such as {{lastExportDateTime}},\nand are referenced from keyConditionExpression and filterExpression."},"onceExportPartitionKey":{"type":"string","description":"Partition key attribute that uniquely identifies items when the export's type is \"once\".\nCeligo uses it to mark items as processed after a successful export, preventing the same\nitems from being exported again on subsequent runs."},"onceExportSortKey":{"type":"string","description":"Sort key attribute used together with onceExportPartitionKey to identify processed items\nwhen the table has a composite primary key. Omit for tables keyed by a partition key\nalone."},"pathToRecords":{"$ref":"#/components/schemas/PathToRecords"},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"}}},"PathToRecords":{"type":"string","maxLength":250,"description":"Path to the array of records inside each document or item fetched from the source; each array element is emitted as an individual record, which keeps records within the 5 MB page-size limit even when the source document exceeds it. Accepts dot notation (e.g. `data.orders`) or JSONPath for values starting with `$`, including unions (`$['orders','invoices'][*]`) and filter expressions (`$.items[?(@.qty>3)]`); a path that resolves to nothing emits zero records for that document or item. When omitted, each document or item is emitted as one record, exactly as before — HTTP and file-based exports configure this same capability through their existing `http.response.resourcePath` and `file.json`/`file.xml` `resourcePath` fields."},"FTP":{"type":"object","description":"Defines which files to retrieve from an FTP, FTPS, or SFTP server. Required when the\n_connectionId field references an FTP/SFTP connection; must not be included for other\nconnection types. directoryPath selects the folder, fileNameStartsWith/fileNameEndsWith\nfilter files by name, and backupDirectoryPath controls where files are moved after\nretrieval.","required":["directoryPath"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies partner-specific B2B settings for this export.\nWhen set, the export inherits the connector's pre-configured settings; omit to use only\nthe FTP connection details."},"directoryPath":{"type":"string","description":"Directory on the server to retrieve files from, either absolute or relative to the login\ndirectory; the FTP user must have read permission on it. Use forward slashes regardless\nof server OS — paths are case-sensitive on UNIX/Linux servers. Supports handlebars\ntemplates, e.g. archive/{{date 'YYYY-MM-DD'}}."},"fileNameStartsWith":{"type":"string","description":"Only retrieves files whose names start with this value (case-sensitive on most servers);\naccepts static text or handlebars templates. When combined with fileNameEndsWith, files\nmust match both."},"fileNameEndsWith":{"type":"string","description":"Only retrieves files whose names end with this value, commonly a file extension\n(case-sensitive on most servers). When combined with fileNameStartsWith, files must\nmatch both."},"backupDirectoryPath":{"type":"string","description":"Directory on the same server where files are moved after successful export, giving you\nan independent backup; if omitted, files are simply deleted from the source directory\nafter successful export unless `file.skipDelete` is true (Celigo also keeps its own copy\nof processed files for a set period). Supports static paths or handlebars templates."}}},"JDBC":{"type":"object","description":"Configuration object for JDBC (Java Database Connectivity) data integration exports.\n\nThis object is REQUIRED when the _connectionId field references a JDBC database connection\nand must not be included for other connection types. It defines how data is extracted\nfrom relational databases using SQL queries.\n\n**Jdbc export capabilities**\n- Execute custom SQL SELECT statements\n- Support for joins, aggregations, and functions\n- Flexible data retrieval from any accessible tables or views\n- Compatible with all major database systems\n**Critical:** WHAT BELONGS IN THIS OBJECT\n- `query` - **ALWAYS REQUIRED** - The SQL SELECT statement\n- `once` - **REQUIRED** when the export's Object Type is `\"once\"` (set _include_once: true)\n- **DO NOT** put `delta` inside this object - delta is handled via the query\n\n**Delta exports (type: \"delta\")**\nFor delta/incremental exports, do NOT populate a `delta` object inside `jdbc`.\nInstead, use `{{lastExportDateTime}}` or `{{currentExportDateTime}}` directly in the query:\n```json\n{\n  \"type\": \"delta\",\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE updatedAt > {{lastExportDateTime}}\"\n  }\n}\n```\n\n**Once exports (type: \"once\")**\nFor once exports (mark records as processed), populate `jdbc.once.query`:\n```json\n{\n  \"type\": \"once\",\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE exported = false\",\n    \"once\": {\n      \"query\": \"UPDATE customers SET exported = true WHERE id = {{record.id}}\"\n    }\n  }\n}\n```\n\n**Standard exports (type: null or not specified)**\nJust provide the query:\n```json\n{\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE status = 'ACTIVE'\"\n  }\n}\n```","required":["query"],"properties":{"formType":{"type":["string","null"],"enum":["sql","simple",null],"description":"Selects how the export's query is authored. Use `sql` for a hand-written SQL statement in\n`query`; use `simple` to build the query from the `simple` object's table/columns/filter."},"query":{"type":"string","description":"SQL SELECT statement executed to retrieve data, from simple table selections to joins and\naggregations. For delta exports, include {{lastExportDateTime}}/{{currentExportDateTime}}\nin the WHERE clause rather than configuring a separate delta object."},"once":{"type":"object","description":"**CRITICAL: REQUIRED when the export's Object Type is \"once\".**\n\nIf Object Type is \"once\", you MUST set _include_once to true (or include this object).\n\nThis object has ONLY ONE property: \"query\" (a SQL UPDATE string).\nDO NOT create any other properties like \"update\", \"table\", \"set\", \"where\", etc.\n\nCORRECT format:\n```json\n{\"query\": \"UPDATE customers SET exported=true WHERE id={{record.id}}\"}\n```\n\nWRONG format (DO NOT DO THIS):\n```json\n{\"update\": {\"table\": \"customers\", \"set\": {...}}}\n```\n","properties":{"query":{"type":"string","description":"**REQUIRED** - A SQL UPDATE statement string to mark records as processed.\n\nThis is a plain SQL UPDATE query string, NOT a structured object.\n\nThe query runs AFTER each record is successfully exported, setting a flag\nto indicate the record has been processed.\n\nFormat: \"UPDATE <table> SET <column>=<value> WHERE <id_column>={{record.<id_field>}}\"\n\nExample: \"UPDATE customers SET exported=true WHERE id={{record.id}}\"\n\nThe {{record.id}} placeholder is replaced with the actual record ID from each exported row.\n"}}},"simple":{"type":"object","description":"Visual query-builder configuration used when `formType` is `simple`; the platform builds\nthe SELECT from these instead of raw SQL. Only relevant when `formType` is `simple`.","properties":{"table":{"type":"string","description":"Table or view to select rows from."},"columns":{"type":"array","items":{"type":"string"},"description":"Columns to return; an empty list selects all columns."},"filter":{"description":"Filter applied as the WHERE clause for the generated query, stored as the\nplatform's structured filter-rules object.","allOf":[{"$ref":"#/components/schemas/Filter"}]}}}}},"MongoDB":{"type":"object","description":"Defines how documents are retrieved from MongoDB collections. Required, with `method` and\n`collection`, when the _connectionId field references a MongoDB connection and the export\nfetches documents; must not be included for other connection types. A change-stream\nlistener (`type: stream`) is configured through `cdc` instead and may omit this object, or\nset only `collection` to scope the listener. Supports find queries with optional filter\ncriteria and field projections.","properties":{"method":{"type":"string","enum":["find"],"description":"MongoDB operation used to retrieve documents. Only \"find\" is currently supported,\nequivalent to db.collection.find(filter, projection)."},"collection":{"type":"string","description":"Name of the MongoDB collection to query. Case-sensitive and must reference an existing\ncollection in the connected database. Required on find exports; on a change-stream\nlistener (`type: stream`) it is optional and may list several collections separated by\ncommas, matching the listener form's \"Collections\" field."},"filter":{"type":"string","description":"MongoDB query document, as a JSON string, that selects which documents to export; omit\nto return every document in the collection. Supports standard MongoDB query operators\nand handlebars variables for dynamic values — for example\n{\"lastModified\": {\"$gt\": \"{{lastRun}}\"}} for incremental processing."},"projection":{"type":"string","description":"MongoDB projection document, as a JSON string, that limits which fields are returned;\nomit to return all fields. Use 1 to include fields or 0 to exclude them — the two modes\ncannot be mixed except for _id, which is included by default unless explicitly excluded.\nProjection affects only the shape of returned documents, not which documents match."},"pipeline":{"type":"string","description":"MongoDB aggregation pipeline, as a JSON-array string of stages, used instead of a plain\nfind when documents need to be transformed, joined, or grouped server-side (e.g. `$match`,\n`$lookup`, `$group`). When set, it takes the place of `filter`/`projection`. Supports\nhandlebars variables for incremental processing."},"readPreference":{"type":"string","enum":["doNotOverride","primary","primaryPreferred","secondary","secondaryPreferred","nearest"],"default":"doNotOverride","description":"Overrides which replica-set member this export reads from, trading consistency for load\ndistribution. Leave at `doNotOverride` to inherit the connection's setting."},"pathToRecords":{"$ref":"#/components/schemas/PathToRecords"},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"}}},"NetSuite":{"type":"object","description":"NetSuite-specific export configuration. Required when `_connectionId` references a NetSuite\nconnection; omit for all other connection types. Supports saved-search, RESTlet, and\ndistributed (SuiteApp) exports, plus two file cabinet modes: blob exports transfer files\nas-is (set the export's top-level `type` to `blob` and configure `netsuite.blob`), while\nfile exports parse file contents into records (leave the export's top-level `type` unset\nand configure `netsuite.file`).","properties":{"type":{"type":"string","enum":["search","basicSearch","metadata","selectoption","restlet","getList","getServerTime","distributed","file"],"description":"Controls how data is retrieved from NetSuite and which sibling object must be configured:\n`search` pairs with `netsuite.searches`, `restlet` with `netsuite.restlet`, `distributed`\nwith `netsuite.distributed`, and `file` with `netsuite.file.folderInternalId`. For blob\nexports (raw file transfer without parsing), leave this field unset, set the export's\ntop-level `type` to `blob`, and configure `netsuite.internalId` instead. For lookups\n(`isLookup: true`), prefer `restlet`, which supports `suiteapp2.0` saved searches with\ndynamic inputs; `search` is limited for dynamic lookups."},"searches":{"type":"array","description":"Saved search configurations that query NetSuite for records. Each entry references a\nsaved search by internal ID, names the record type being searched, and can add filter\ncriteria.","items":{"type":"object","properties":{"savedSearchId":{"type":"string","description":"Internal ID of the NetSuite saved search to run."},"recordType":{"type":"string","description":"NetSuite record type being searched, as its exact lowercase script ID\n(e.g. \"customer\", \"salesorder\") — not the display name."},"criteria":{"type":"array","description":"Additional filter criteria applied on top of the saved search.","items":{"type":"object","properties":{"field":{"type":"string","description":"Script-id of the search column to filter on.  REQUIRED.\nMust come from the record type's SEARCH COLUMNS list\n(NOT the body-fields list).  Becomes the `name`\nargument of NetSuite's `search.createFilter()`."},"operator":{"type":"string","description":"Comparison operator.  REQUIRED.  Supported values\ndepend on the column's data type (e.g. `is`,\n`isnot`, `after`, `before`, `anyof`,\n`noneof`, `contains`, `startswith`,\n`greaterthan`, `lessthan`).  Must be a string\nNetSuite recognises -- mismatched operators cause\nruntime rejection."},"join":{"type":"string","description":"Optional join name when filtering through a related\nrecord (e.g. `customer` from a sales order).  Use\nonly when the column lives on a related record and\nNetSuite supports the join from the base record type."},"searchValue":{"description":"Value (or array of values for `anyof`-style\noperators) to compare against the column.  Required\nfor most operators; omit for the few unary operators\n(e.g. `isempty`, `isnotempty`)."}},"required":["field","operator"]}}}}},"metadata":{"type":"object","description":"Supplemental metadata associated with the NetSuite export."},"selectoption":{"type":"object","description":"Configuration for `selectoption` exports, which retrieve the available select options\nfor a NetSuite field."},"customFieldMetadata":{"type":"object","description":"Metadata describing the custom fields defined in the NetSuite account."},"skipGrouping":{"type":"boolean","description":"When true, each saved-search result row becomes its own record. When false or omitted,\nconsecutive result rows that share the record's internal id are grouped into one record\nshaped as an array of rows — the *Group rows* option in the UI."},"statsOnly":{"type":"boolean","description":"When true, returns only summary statistics about matching records instead of the\ndetailed records themselves."},"internalId":{"type":"string","description":"Internal ID of the file in the NetSuite file cabinet to export. Required for blob\nexports (the export's top-level `type` is `blob`); the file is transferred as-is\nwithout parsing. For parsed file exports, use `netsuite.file.folderInternalId` instead."},"blob":{"type":"object","properties":{"purgeFileAfterExport":{"type":"boolean","description":"When true, permanently deletes the file from the NetSuite file cabinet after a\nsuccessful export. When false or omitted, the file is left in place."}},"description":"Configuration for blob exports, which transfer files from the NetSuite file cabinet\nas-is without parsing them into records. To export a blob, set the export's top-level\n`type` to `blob`, set `netsuite.internalId` to the file's internal ID, and leave\n`netsuite.type` unset. Use `netsuite.file` instead when file contents should be parsed\ninto records."},"restlet":{"type":"object","properties":{"recordType":{"type":"string","description":"NetSuite record type the RESTlet operates on, as its script ID. Custom record\ntypes use the \"customrecord_\" prefix."},"batchSize":{"type":"number","description":"Number of records the RESTlet returns per request (default 1000). This, not the\nexport's `pageSize`, sizes each NetSuite call; the export keeps requesting until the\nsearch is exhausted. Larger batches reduce round-trips but raise NetSuite governance\nand memory use per call; tune to the record's size. The RESTlet caps a call at 1000,\nso a larger value acts as 1000. In a test run or preview the request is capped at the\nexport's `pageSize` (default 20) instead."},"searchId":{"type":["string","null"],"description":"Internal ID of the NetSuite saved search the RESTlet executes. Results follow the\nsaved search's current definition, so editing the search in NetSuite changes the\nexport output without changing this ID."},"useSS2Restlets":{"type":"boolean","description":"When true, calls SuiteScript 2.0 RESTlets. When false or omitted, legacy\nSuiteScript 1.0 RESTlets are used.\nSet at step creation together with restletVersion — the same\ncreation-time-only constraint applies (see restletVersion)."},"restletVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"RESTlet version the export invokes. Defaults to `suiteapp2.0` when\n`useSS2Restlets` is true, `suitebundle` otherwise.\nThe version is fixed when the step is created — the Advanced selector is\ndisabled on existing steps, and migrating an existing export to a\ndifferent version means recreating or cloning it."},"criteria":{"type":"array","description":"Filter conditions added on top of the saved search (the UI's \"Additional search\ncriteria\"). Each condition pairs a search column with an operator and one or two\ncomparison values, and the conditions are combined with AND — every one must match.","items":{"type":"object","properties":{"field":{"type":"string","description":"Script-id of the search column to filter on. This is the primary\nfield the criterion is evaluated against (e.g. \"trandate\", \"status\")."},"join":{"type":"string","description":"Join relationship that applies this filter to a related record's fields instead\nof the base record. Must match a join name NetSuite defines for the record type."},"operator":{"type":"string","enum":["after","allof","any","anyof","before","between","contains","doesnotcontain","doesnotstartwith","equalto","greaterthan","greaterthanorequalto","haskeywords","is","isempty","isnot","isnotempty","lessthan","lessthanorequalto","noneof","notafter","notallof","notbefore","notbetween","notequalto","notgreaterthan","notlessthan","notlessthanorequalto","noton","notonorafter","notonorbefore","notwithin","on","onorafter","onorbefore","startswith","within"],"description":"Comparison operator applied between the column and the search value, using\nNetSuite's lowercase search operator names. Which operators a column accepts\ndepends on its data type."},"searchValue":{"description":"Value the column is compared against — a string, a number, or an array of\nvalues for `anyof`-style operators. On lookup steps it may be a Handlebars\nexpression rendered from the incoming record (for example `{{poNumber}}`)."},"searchValue2":{"description":"Second comparison value for the two-value operators `between` and `notbetween`\n(the upper bound). Omit for every other operator."},"formula":{"type":"string","description":"NetSuite formula expression used as the filter condition, for criteria that\nstandard field-operator-value comparisons can't express."}}}},"columns":{"type":"array","description":"Search columns the RESTlet returns for each matching record. With a saved search,\nthe RESTlet returns the search's own result columns followed by any listed here: a\nlist adds columns and never removes one of the search's, so remove a column in the\nsaved search itself. An ad-hoc search (no `searchId`) must list its columns here.","items":{"type":"object","properties":{"_id":{"type":"string","readOnly":true,"description":"Subdocument id the server assigns to the column entry on save. Not a NetSuite\nfield id — never set it when authoring."},"name":{"type":"string","description":"Column field name as referenced in the search."},"join":{"type":"string","description":"Join relationship that sources this column from a related record instead of\nthe base record. Supports dot notation for nested joins (e.g. \"employee.manager\")."},"summary":{"type":"string","enum":["group","sum","count","avg","min","max"],"description":"Summary type applied to this column, turning the search into a summary search."},"formula":{"type":"string","description":"NetSuite formula expression that computes this column's value at runtime, such\nas a CASE statement or date formatting, instead of reading a stored field."},"label":{"type":"string","description":"Display name for the column; it becomes the property name of the column's value\nin each exported record."},"sort":{"type":"boolean","description":"When true, query results are sorted by this column's values."}}}},"markExportedBatchSize":{"type":"integer","description":"Number of records updated per RESTlet call when marking records as exported in\nNetSuite (live: an integer count, e.g. 100).","maximum":100,"minimum":1},"hooks":{"type":["object","null"],"properties":{"batchSize":{"type":"number","description":"Legacy location of the RESTlet page size; `restlet.batchSize` is the current\nfield and takes precedence when both are set."},"preSend":{"type":"object","properties":{"fileInternalId":{"type":"string","description":"Internal ID of the JavaScript file in the NetSuite File Cabinet that defines\nthe hook function. Must be readable by the role behind the export's connection."},"function":{"type":"string","description":"Name of the global function in that file that NetSuite invokes as the pre\nsend hook."},"configuration":{"type":"object","description":"Free-form JSON stored with the hook reference for the script's own use. Custom\nsettings in scope for the export are passed to the function separately as\n`options.settings`."}},"description":"SuiteScript hook that runs inside NetSuite after a page of records is collected\nand before it is sent to the Celigo platform."}},"description":"SuiteScript hooks for the export. Only `preSend` exists on exports; imports carry\nthe pre map, post map, and post submit hooks."},"cLocked":{"type":"object","description":"Lock state that prevents modification of the configuration when set."}},"description":"Configuration for `restlet` exports. Identifies the RESTlet script that retrieves data\nfrom NetSuite, along with the saved search, criteria, and columns it executes with."},"distributed":{"type":"object","properties":{"recordType":{"type":"string","description":"NetSuite record type the listener is deployed on, as its lowercase script ID\n(`customer`, `salesorder`, `customrecord_<scriptid>`) — never the display name or a\ncustom record's numeric internal ID. Types the platform's record-type catalog marks\n`doesNotSupportRTExport` cannot host a listener and are not offered in the UI."},"executionContext":{"type":"array","description":"NetSuite execution contexts that trigger this distributed export. A record change\nfires the export only when it occurs in one of the listed contexts.","default":["userinterface","webstore"],"items":{"type":"string","enum":["userinterface","webservices","csvimport","offlineclient","portlet","scheduled","suitelet","custommassupdate","workflow","webstore","userevent","mapreduce","restlet","webapplication","restwebservices"]}},"disabled":{"type":"boolean","description":"When true, the listener's NetSuite deployment is inactive and record saves no longer\ntrigger the export. Maintained by the platform as the flow is disabled and re-enabled;\nre-enabling the flow re-registers the deployment. A listener is created disabled, and\na flow created already enabled leaves it that way until a flow update sets\n`disabled: false`, which activates it within seconds."},"executionType":{"type":"array","description":"Record operations that trigger this distributed export. A record event fires the\nexport only when its operation matches one of the listed types.","default":["create","edit","xedit"],"items":{"type":"string","enum":["create","edit","delete","xedit","copy","view","cancel","approve","reject","pack","ship","markcomplete","reassign","editforecast","dropship","specialorder","orderitems","paybills","print","email"]}},"qualifier":{"type":["array","string","null"],"description":"Field-specific qualification criteria the SuiteApp evaluates against the record before\nexporting it; saves that do not match are discarded. Stored as a filter-expression array\nof `[field, operator, value]` rules joined by lowercase `\"and\"` / `\"or\"`, as a legacy\nexpression string, or null when no criteria are set."},"skipExportFieldId":{"type":"string","description":"Script ID of a checkbox body field on the record type. A save with that box checked is\nskipped by the listener, which then clears the box itself — used to keep a mass update\nfrom fanning out and to stop a write-back import from re-triggering the listener."},"hooks":{"type":"object","description":"SuiteScript hook that runs inside NetSuite before the record leaves it.","properties":{"preSend":{"type":"object","description":"Pre-send hook: a SuiteScript function in the NetSuite file cabinet invoked before\nrecords leave NetSuite, with the full SuiteScript API available — for example to run\ndynamic searches against the record being exported.","properties":{"fileInternalId":{"type":"string","description":"Internal ID of the file cabinet file that holds the hook's SuiteScript."},"function":{"type":"string","description":"Name of the function in that file to invoke for each export."},"configuration":{"type":"object","description":"Settings passed to the hook function."}}}}},"sublists":{"type":["array","null"],"description":"IDs of the NetSuite sublists to include with each exported record (for example `item`\nfor transaction line items). Records are exported with body fields only when no\nsublists are selected. Legacy documents may store null (equivalent to no sublists).","items":{"type":"string"}},"referencedFields":{"type":"object","description":"Field references recorded on the listener by the platform. Not exposed in the listener\nform; leave as stored."},"relatedLists":{"type":"object","description":"Related record lists recorded on the listener by the platform. Not exposed in the\nlistener form; leave as stored."},"forceReload":{"type":"boolean","description":"When true, the listener reloads the record from the database after the save before\nexporting it, so fields that are populated only once the save completes — such as\ntransaction line IDs on new lines — are included. The reload slows every save of that\nrecord type."},"ioEnvironment":{"type":"string","description":"integrator.io environment the listener's NetSuite deployment reports to. Written by the\nplatform when the listener registers with NetSuite; live-observed values include\n`production` and `staging`."},"ioDomain":{"type":"string","description":"integrator.io domain the listener's NetSuite deployment calls back to — the account's\nown regional domain, stamped by the platform on every save and echoed on create and\nupdate responses; not returned by GET."},"lastSyncedDate":{"type":"string","format":"date-time","description":"When the listener's deployment last synchronized with NetSuite. Written by the platform;\nnot set until the first successful synchronization."},"settings":{"type":"object","description":"Additional key-value settings for the distributed export. Not exposed in the listener\nform."},"useSS2Framework":{"type":"boolean","description":"Derived by the platform from `frameworkVersion` — true when it is `suiteapp2.0`, absent\notherwise — and echoed on create and update responses; not returned by GET. A value\nsent on create or update is replaced by the derived one."},"frameworkVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"SuiteApp framework version used by the distributed export.\nFixed when the step is created — the Advanced selector is disabled on\nexisting steps, and migrating an existing listener to a different\nversion means recreating or cloning it."}},"description":"Configuration for `distributed` exports, which use the NetSuite SuiteApp to fire\nreal-time, event-driven exports when records change. Define the record type to listen\nto and the execution contexts and operations that trigger it."},"getList":{"type":"array","description":"Configuration for `getList` exports — a list of record references, each retrieving one\nNetSuite record by its identifier. The export returns the fetched records in list order.","items":{"type":"object","properties":{"type":{"type":"string","description":"Standard NetSuite record type to retrieve (e.g. \"customer\", \"salesOrder\").\nUsed when `typeId` is absent. Case-sensitive."},"typeId":{"type":"string","description":"Script ID of a custom record type (\"customrecord_...\") or custom transaction\ntype (\"customtransaction_...\"). Takes precedence over `type` when present.\nCase-sensitive."},"internalId":{"type":"string","description":"Internal ID of the NetSuite record to retrieve. Assigned by NetSuite at record\ncreation and unique within the record type. Takes precedence over `externalId`."},"externalId":{"type":"string","description":"Identifier assigned by an external system, used to reference and synchronize the\nrecord across systems. Distinct from the NetSuite-assigned internal ID."}}}},"searchPreferences":{"type":"object","properties":{"bodyFieldsOnly":{"type":"boolean","description":"When true, search results include only the record's body fields and exclude fields\nfrom joined or related records. Reduces payload size when related data isn't needed."},"pageSize":{"type":"number","description":"Number of search results NetSuite returns per page."},"returnSearchColumns":{"type":"boolean","description":"When true, search results include the column data defined in the search. When\nfalse, column data is omitted and results contain only minimal record information."}},"description":"Preferences that control how NetSuite executes searches and shapes the results it returns."},"file":{"type":"object","description":"Configuration for file exports, which retrieve files from the NetSuite file cabinet\nand parse their contents (CSV, XML, JSON) into records. Leave the export's top-level\n`type` unset when using this object; for raw transfers without parsing, use\n`netsuite.blob` with the export's `type` set to `blob` instead.","properties":{"folderInternalId":{"type":"string","description":"Internal ID of the NetSuite file cabinet folder to export files from. Accepts a\nhandlebars expression (e.g. `{{record.folderId}}`) when the folder must be\nselected dynamically from record data. The ID is stable across folder renames and\nmoves, but may differ between non-production and production accounts."},"backupFolderInternalId":{"type":"string","description":"Internal ID of the NetSuite file cabinet folder where backup files are stored.\nIDs are account-specific and do not transfer between non-production and production\nenvironments."},"fileNameStartsWith":{"type":"string","description":"Optional prefix filter. Only files whose names start with this string are\nexported from the configured folder."},"fileNameEndsWith":{"type":"string","description":"Optional suffix filter. Only files whose names end with this string (e.g. a\n\".csv\" extension) are exported from the configured folder."}}}},"required":[]},"RDBMS":{"type":"object","description":"Configuration object for Relational Database Management System (RDBMS) data integration exports.\n\nThis object defines how data is read from a relational database and must not be included\nfor other connection types. For query-type exports (standard, delta, once) it is REQUIRED\nwhen the _connectionId field references an RDBMS database connection and holds the SQL\nquery. For real-time stream listeners (`type: \"stream\"`) it holds the CDC watch list\n(`tables`) instead — and a PostgreSQL listener scoped by a publication\n(`cdc.publicationName`) may omit this object entirely.\n\n**Rdbms export capabilities**\n- Execute custom SQL SELECT statements\n- Support for joins, aggregations, and functions\n- Flexible data retrieval from any accessible tables or views\n- Compatible with all major database systems\n\n**Critical:** WHAT BELONGS IN THIS OBJECT\n- `query` - The SQL SELECT statement - required for every query-type export (standard, delta, once)\n- `once` - **REQUIRED** when the export's Object Type is `\"once\"` (set _include_once: true)\n- `tables` - the CDC listener watch list - real-time stream listeners (`type: \"stream\"`) only\n- **DO NOT** put `delta` inside this object - delta is handled via the query\n- **DO NOT** author `query` on a stream listener - real-time CDC exports have no SQL query\n\n**Delta exports (type: \"delta\")**\nFor delta/incremental exports, do NOT populate a `delta` object inside `rdbms`.\nInstead, use `{{lastExportDateTime}}` or `{{currentExportDateTime}}` directly in the query, and\nset the export's top-level `delta.startDate`: without it the first run's window is empty.\n```json\n{\n  \"type\": \"delta\",\n  \"delta\": { \"startDate\": \"2026-01-01T00:00:00.000Z\" },\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE updatedAt > {{lastExportDateTime}}\"\n  }\n}\n```\n\n**Once exports (type: \"once\")**\nFor once exports (mark records as processed), populate `rdbms.once.query`:\n```json\n{\n  \"type\": \"once\",\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE exported = false\",\n    \"once\": {\n      \"query\": \"UPDATE customers SET exported = true WHERE id = {{record.id}}\"\n    }\n  }\n}\n```\n\n**Standard exports (type: null or not specified)**\nJust provide the query:\n```json\n{\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE status = 'ACTIVE'\"\n  }\n}\n```\n\n**Real-time CDC listener exports (type: \"stream\")**\nSQL Server and PostgreSQL connections support change-data-capture listeners that stream row\nchanges continuously (the `cdc` object holds the listener settings). A stream export has NO\n`query` — it watches `tables`:\n```json\n{\n  \"type\": \"stream\",\n  \"rdbms\": { \"tables\": \"public.orders,public.customers\" },\n  \"cdc\": { \"properties\": [{ \"name\": \"snapshot.mode\", \"value\": \"no_data\" }] }\n}\n```\nA PostgreSQL listener can instead scope tables through a publication\n(`cdc.publicationName`), in which case `tables` stays unset and the `rdbms` object may be\nomitted entirely.","properties":{"tables":{"type":"string","description":"Comma-separated list of tables the CDC listener watches for changes. Used only by real-time\nlistener exports (`type` is `stream`); standard SQL exports use `query` instead. SQL Server\nlisteners use fully-qualified three-part names (`database.schema.table`); PostgreSQL\nlisteners use two-part names (`schema.table`)."},"query":{"type":"string","description":"SQL SELECT statement executed to retrieve data, from simple table selections to joins,\naggregations, and parameterized queries with Handlebars expressions. Query-type exports\nonly — real-time stream listeners (`type` is `stream`) have no SQL query. For delta exports,\nreference {{lastExportDateTime}}/{{currentExportDateTime}} directly in the query rather\nthan configuring a separate delta object.\n\nDouble braces render a string as a quoted SQL literal with its apostrophes escaped, and\ntriple braces insert a value raw. Observed in Snowflake queries:\n\n- String helper output (`dateFormat`, `jsonSerialize`) and both delta variables are\n  quoted the same way; a number or boolean renders bare, whether it is a JSON value or a\n  numeric helper's result (`{{add 2 3}}` renders `5`).\n- In a run, {{lastExportDateTime}} and {{currentExportDateTime}} arrive as quoted ISO 8601\n  UTC literals with milliseconds (`'2026-01-15T00:00:00.000Z'`), or in `delta.dateFormat`\n  when it is set: add no quotes of your own, and don't parse them with a format mask\n  written for another format.\n- A preview of an unsaved export document renders both as empty, unquoted text, so a\n  delta query fails to compile there unless the document carries both in `postData`.\n- The quoting leaves backslashes, which Snowflake reads as escape sequences, so a\n  double-brace value holding a backslash arrives changed. Pass such a value in a\n  dollar-quoted constant with triple braces (`$${{{record.field}}}$$`), which Snowflake\n  reads as is, but only when it can't contain `$$`: a `$$` ends the constant, and the rest\n  is read as SQL."},"once":{"type":"object","description":"**CRITICAL: REQUIRED when the export's Object Type is \"once\".**\n\nIf Object Type is \"once\", you MUST set _include_once to true (or include this object).\n\nThis object has ONLY ONE property: \"query\" (a SQL UPDATE string).\nDO NOT create any other properties like \"update\", \"table\", \"set\", \"where\", etc.\n\nCORRECT format:\n```json\n{\"query\": \"UPDATE customers SET exported=true WHERE id={{record.id}}\"}\n```\n\nWRONG format (DO NOT DO THIS):\n```json\n{\"update\": {\"table\": \"customers\", \"set\": {...}}}\n```\n","properties":{"query":{"type":"string","description":"**REQUIRED** - A SQL UPDATE statement string to mark records as processed.\n\nThis is a plain SQL UPDATE query string, NOT a structured object.\n\nThe query runs AFTER each record is successfully exported, setting a flag\nto indicate the record has been processed.\n\nFormat: \"UPDATE <table> SET <column>=<value> WHERE <id_column>={{record.<id_field>}}\"\n\nExample: \"UPDATE customers SET exported=true WHERE id={{record.id}}\"\n\nThe {{record.id}} placeholder is replaced with the actual record ID from each exported row.\n"}}}}},"S3":{"type":"object","description":"Defines which files to retrieve from an Amazon S3 bucket. Required when the _connectionId\nfield references an AWS S3 connection; must not be included for other connection types.\nregion and bucket locate the source, keyStartsWith/keyEndsWith filter objects by key, and\nbackupBucket with keyPrefix controls where files are moved after retrieval.","required":["region","bucket"],"properties":{"region":{"type":"string","default":"us-east-1","description":"AWS region where the bucket is located. Case-insensitive; the value is normalized to\nlowercase."},"bucket":{"type":"string","description":"S3 bucket to retrieve files from. The connection's AWS credentials must have\ns3:ListBucket and s3:GetObject permissions on it."},"keyStartsWith":{"type":"string","description":"Only retrieves objects whose keys start with this value (case-sensitive), effectively\nselecting a folder in S3's flat key structure. When combined with keyEndsWith, objects\nmust match both."},"keyEndsWith":{"type":"string","description":"Only retrieves objects whose keys end with this value (case-sensitive), commonly a file\nextension. When combined with keyStartsWith, objects must match both."},"backupBucket":{"type":"string","description":"Bucket in the same region where files are moved after successful export, giving you an\nindependent backup; if omitted, files are simply deleted from the source bucket after\nsuccessful export unless `file.skipDelete` is true (Celigo also keeps its own copy of\nprocessed files for a set period). The connection's AWS credentials must have\ns3:PutObject permission on this bucket."},"keyPrefix":{"type":"string","description":"Prefix prepended to each file's name when it is moved to the backup bucket; accepts\nstatic text or handlebars templates. The original directory structure is not preserved —\nonly the filename is appended to this prefix. Ignored unless backupBucket is set."}}},"Wrapper":{"type":"object","description":"Configuration for Wrapper exports, which delegate data retrieval to custom connector code\n(typically a stack-hosted function) rather than a built-in adaptor. Required when the\n_connectionId field references a wrapper connection.","required":["function"],"properties":{"function":{"type":"string","description":"Name of the function the wrapper invokes to retrieve records. Must match a callable\nfunction in the wrapper's execution context; names are case-sensitive."},"configuration":{"oneOf":[{"type":"object","additionalProperties":true},{"type":"array","maxItems":0}],"description":"Free-form settings passed to the wrapper function at runtime (connector-specific keys such\nas method, apiVersion, headers, relativePath, or body). Structure is defined by the wrapper\ncode, not by this schema. May be stored as an empty array when no settings exist."}}},"Parsers":{"type":"array","description":"Configuration for parsing XML payloads (i.e. files, HTTP responses, etc.). Use this field when you need to process XML data\nand transform it into a JSON records.\n\n**Implementation notes**\n\n- This is where you configure how to parse XML data in your resource\n- Although defined as an array, you typically only need a single parser configuration\n- Currently only XML parsing is supported\n- Only configure this field when working with XML data that needs structured parsing\n","items":{"type":"object","properties":{"version":{"type":"string","description":"Version identifier for the parser configuration format. Currently only version \"1\" is supported.\n\nAlways set this field to \"1\" as it's the only supported version at this time.\n","enum":["1"]},"type":{"type":"string","description":"Defines the type of parser to use. Currently only \"xml\" is supported.\n\nWhile the system is designed to potentially support multiple parser types in the future,\nat this time only XML parsing is implemented, so this field must be set to \"xml\".\n","enum":["xml"]},"name":{"type":"string","description":"Optional identifier for the parser configuration. This field is primarily for documentation\npurposes and is not functionally used by the system.\n\nThis field can be omitted in most cases as it's not required for parser functionality.\n"},"rules":{"type":"object","description":"Configuration rules that determine how XML data is parsed and converted to JSON.\nThese settings control the structure and format of the resulting JSON records.\n\n**Parsing options**\n\nThere are two main parsing strategies available:\n- **Automatic parsing**: Simple but produces more complex output\n- **Custom parsing**: More control over the resulting JSON structure\n","properties":{"V0_json":{"type":"boolean","description":"Controls the XML parsing strategy.\n\n- When set to **true** (Automatic): XML data is automatically converted to JSON without\n  additional configuration. This is simpler to set up but typically produces more complex\n  and deeply nested JSON that may be harder to work with.\n\n- When set to **false** (Custom): Gives you more control over how the XML is converted to JSON.\n  This requires additional configuration (like listNodes) but produces cleaner, more\n  predictable JSON output.\n\nMost implementations use the Custom approach (false) for better control over the output format.\n"},"listNodes":{"type":"array","description":"Paths of elements that always parse as arrays. An element that can repeat parses as an\nobject when it occurs once and as an array when it occurs more than once, so list every\nrepeating element here to give each record the same shape regardless of item count.\nEach entry is the element's full path from the record element selected by the resource\npath (from the document root when there is no resource path), with an optional leading\nslash. Only relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"includeNodes":{"type":"array","description":"Limits which XML nodes are included in the output JSON.\n\nFor large XML documents, you can use this field to extract only the nodes you need,\nreducing the size and complexity of the resulting JSON. Only nodes specified here\n(and their children) will be included in the output.\n\nEach entry should be a simplified XPath expression pointing to nodes to include.\n\nOnly relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"excludeNodes":{"type":"array","description":"Specifies which XML nodes should be excluded from the output JSON.\n\nSometimes it's easier to specify which nodes to exclude rather than which to include.\nUse this field to identify nodes that should be omitted from the output JSON.\n\nEach entry should be a simplified XPath expression pointing to nodes to exclude.\n\nOnly relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"stripNewLineChars":{"type":"boolean","description":"Controls whether newline characters are removed from text values.\n\nWhen set to true, all newline characters (\\n, \\r, etc.) will be removed from\ntext content in the XML before conversion to JSON.\n","default":false},"trimSpaces":{"type":"boolean","description":"Controls whether leading and trailing whitespace is trimmed from text values.\n\nWhen set to true, all values will have leading and trailing whitespace removed\nbefore conversion to JSON.\n","default":false},"attributePrefix":{"type":"string","description":"Specifies a character sequence to prepend to XML attribute names when converted to JSON properties.\n\nIn XML, both elements and attributes can exist at the same level, but in JSON this distinction is lost.\nTo maintain the distinction between element data and attribute data in the resulting JSON, this prefix\nis added to attribute names during conversion.\n\nFor example, with attributePrefix set to \"Att-\" and an XML element like:\n```xml\n<product id=\"123\">Laptop</product>\n```\n\nThe resulting JSON would be:\n```json\n{\n  \"product\": \"Laptop\",\n  \"Att-id\": \"123\"\n}\n```\n\nThis helps maintain the distinction between element content and attribute values in the\nconverted JSON, making it easier to reference specific data in downstream processing steps.\n"},"textNodeName":{"type":"string","description":"Specifies the property name to use for element text content when an element has both\ntext content and child elements or attributes.\n\nWhen an XML element contains both text content and other nested elements or attributes,\nthis field determines what property name will hold the text content in the resulting JSON.\n\nFor example, with textNodeName set to \"value\" and an XML element like:\n```xml\n<item id=\"123\">\n  Laptop\n  <category>Electronics</category>\n</item>\n```\n\nThe resulting JSON would be:\n```json\n{\n  \"item\": {\n    \"value\": \"Laptop\",\n    \"category\": \"Electronics\",\n    \"id\": \"123\"\n  }\n}\n```\n\nThis allows for unambiguous parsing of complex XML structures that mix text content with\nchild elements. Choose a name that's unlikely to conflict with actual element names in your XML.\n"}}}}}},"Mappings":{"type":"array","description":"Array of field mapping configurations for transforming data from one format into another.\n\n**Guidance**\n\nThis schema is designed around RECURSION as its core architectural principle. Understanding this recursive\nnature is essential for building effective mappings:\n\n1. The schema is self-referential by design - a mapping can contain nested mappings of the same structure\n2. Complex data structures (nested objects and arrays of objects, to any depth) are ALL\n   handled through this recursive pattern; an array of arrays is not (build one in a script hook)\n3. Each mapping handles one level of the data structure; deeper levels are handled by nested mappings\n\nWhen generating mappings programmatically:\n- For simple fields (string, number, boolean): Create single mapping objects\n- For objects: Create a parent mapping with nested 'mappings' array containing child field mappings\n- For arrays: Use 'buildArrayHelper' with extract paths defining array inputs and\n  recursive 'mappings' to define object structures\n\nThe system will process these nested structures recursively during runtime, ensuring proper construction\nof complex hierarchical data while maintaining excellent performance.\n\nOn save, a leaf row with neither a source (`extract`, `hardCodedValue`, `lookupName`) nor a\ndestination (`generate`) is dropped silently, at every nesting level, and so is an object or\nobject-array row left with no source, no destination and no remaining child `mappings` or\n`buildArrayHelper` entries; a row with only one of the two, and a source-less, destination-less\nwrapper that still has children, are kept.\n\nThe shape of the array is the mapper's **JSON structure**. Named top-level items produce an object\nrecord (`record { }`). Exactly one top-level item with `dataType: objectarray` and no `generate` is\nthe `rows [ ]` structure: the mapped record is an array — usually one element per element of the\nsource array that root's `buildArrayHelper` iterates — Handlebars templates such as `http.body`\nbind it as `rows` instead of `record`, and the mapping editor shows that row's destination disabled.\n","items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}}},"items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}},"Transform":{"type":"object","description":"Configuration for transforming data during processing operations. This object enables\nreshaping of records.\n\n**Transformation capabilities**\n\nCeligo's transformation engine offers powerful features for data manipulation:\n- Precise field mapping with JSONPath expressions\n- Support for any level of nested arrays\n- Formula-based field value generation\n- Dynamic references to flow and integration settings\n\n**Implementation approaches**\n\nThere are two distinct transformation mechanisms available:\n\n**Rule-Based Transformation (`type: \"expression\"`)**\n- **Best For**: Most transformation scenarios from simple to complex\n- **Capabilities**: Field mapping, formula calculations, lookups, nested data handling\n- **Advantages**: Visual configuration, no coding required, intuitive interface\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear mapping requirements or need to reshape data structure\n\n**Script-Based Transformation (`type: \"script\"`)**\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Capabilities**: Full programmatic control, custom processing, complex business rules\n- **Advantages**: Maximum flexibility, can implement any transformation logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Visual transformation tools aren't sufficient for your use case\n","properties":{"type":{"type":"string","description":"Determines which transformation mechanism to use. This choice affects which properties\nmust be configured and how transformation logic is implemented.\n\n**Available types**\n\n**Rule-Based Transformation (`\"expression\"`)**\n- **Required Config**: The `expression` object with mapping definitions\n- **Behavior**: Applies declarative rules to reshape data\n- **Best For**: Most transformation scenarios from simple to complex\n- **Advantages**: Visual configuration, no coding required\n\n**Script-Based Transformation (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to transform data\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard data transformations, use `\"expression\"`\n2. For complex logic or specialized processing, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based transformations. This object enables reshaping data\nwithout requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define transformation rules that\ncan map, modify, and generate data elements.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Field mappings define how input data is transformed to target fields\n- Formulas can be used to calculate or generate new values\n- Lookups can enrich data by fetching related information\n- Mode determines how records are processed (create new or modify existing)\n","properties":{"version":{"type":"string","description":"Version of the expression format. Determines which rules\nproperty contains the transformation logic.\n","enum":["1","2"]},"rules":{"type":"array","description":"Transformation rules for version 1 expressions. An array of\nrule groups; each group is an array of field-mapping objects.\nMost transforms have a single group. Present when `version`\nis `\"1\"`. The output record contains ONLY the generated\nfields — every unmapped field is dropped (v1 has no\nequivalent of Transform 2.0's `modify` mode), and the\nrecord's trace key does not survive the rebuild.\n","items":{"type":"array","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path to read from. Supports multiple\nsyntaxes: bare field names (`id`), dot notation\n(`fulfillment.shipment_id`), slash-prefixed paths\nfor XML (`/FeedProcessingStatus`), wildcards (`*.id`,\n`*.[Internal ID]`), and array indexing (`SDF[0]`).\n"},"generate":{"type":"string","description":"Target field name to write to. Typically a bare name\n(`id`) or dot path (`SDF.Filter.ID`).\n"},"key":{"type":"string","description":"Auto-generated identifier for this rule, used by the\nUI to track individual rules for editing and reordering.\n"}},"required":["extract","generate"]}}},"rulesTwoDotZero":{"type":"object","description":"Configuration for version 2 transformation rules. This object contains the core logic\nfor how data is mapped, enriched, and transformed.\n\n**Capabilities**\n\nTransformation 2.0 provides:\n- Precise field mapping with JSONPath expressions\n- Support for deeply nested data structures\n- Formula-based field generation\n- Dynamic lookups for data enrichment\n- Multiple operating modes to fit different scenarios\n","properties":{"mode":{"type":"string","description":"Transformation mode that determines how records are handled during processing.\n\n**Available modes**\n\n**Create Mode (`\"create\"`)**\n- **Behavior**: Builds entirely new output records from inputs\n- **Use When**: Output structure differs significantly from input\n- **Advantage**: Clean slate approach, no field inheritance\n\n**Modify Mode (`\"modify\"`)**\n- **Behavior**: Makes targeted edits to existing records\n- **Use When**: Output structure should remain similar to input\n- **Advantage**: Preserves unmapped fields from the original record\n","enum":["create","modify"]},"mappings":{"$ref":"#/components/schemas/Mappings"},"lookups":{"allOf":[{"description":"Shared lookup tables used across all mappings defined in the transformation rules.\n\n**Purpose**\n\nLookups provide centralized value translation that can be referenced from any mapping\nin your transformation configuration. They enable consistent translation of codes, IDs,\nand values between systems without duplicating translation logic.\n\n**Usage in transformations**\n\nLookups are particularly valuable in transformations for:\n\n- **Data Normalization**: Standardizing values from diverse source systems\n- **Code Translation**: Converting between different coding systems (e.g., status codes)\n- **Field Enrichment**: Adding descriptive values based on ID or code lookups\n- **Cross-Reference Resolution**: Mapping identifiers between integrated systems\n\n**Implementation**\n\nLookups are defined once in this array and referenced by name in mappings:\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"statusMapping\",\n    \"map\": {\n      \"A\": \"Active\",\n      \"I\": \"Inactive\",\n      \"P\": \"Pending\"\n    },\n    \"default\": \"Unknown Status\"\n  }\n]\n```\n\nThen referenced in mappings using the lookupName property:\n\n```json\n{\n  \"generate\": \"status\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.statusCode\",\n  \"lookupName\": \"statusMapping\"\n}\n```\n\nThe system automatically applies the lookup during transformation processing.\n\nFor complete details on lookup properties and behavior, see the Lookups schema.\n"},{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the JSON shape the transformTwoDotZero processor\nevaluates `mappings[].extract` JSONPath values against at\nflow runtime. Applies only to Transform 2.0 (v2,\n`rulesTwoDotZero`); v1 transforms (the `rules` array on\n`transform.expression.rules`) and script-mode transforms\nignore this field.\n"}}}}},"script":{"type":"object","description":"Configuration for programmable script-based transformations. This object enables complex, custom\ntransformation logic beyond what expression-based transformations can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to transform data according to\nspecialized business rules or complex algorithms.\n\n**Implementation approach**\n\nScript-based transformation works by:\n1. Executing the specified function from the referenced script\n2. Passing input data to the function\n3. Using the function's return value as the transformed output\n\n**Common use cases**\n\nScript transformation is ideal for:\n- Complex business logic that can't be expressed through mappings\n- Algorithmic transformations requiring computation\n- Dynamic transformations based on external factors\n- Legacy system data format compatibility\n- Multi-stage processing with intermediate steps\n\nOnly use script-based transformation when expression-based transformation is insufficient.\nScript transformation requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to a predefined script resource containing the transformation logic.\n\nThe referenced script should contain the function specified in the\n'function' property. It must be a script in this account: any other id fails the save\nwith 422 (\"Only a script owned by the user or a script shared by the Integration App can\nbe used at transform.script._scriptId.\").\n","format":"objectid"},"function":{"type":"string","description":"Name of the function within the script to execute for transformation. This function\nmust exist in the script referenced by _scriptId.\n"}}}}},"Lookups":{"type":"array","description":"Configuration for value-to-value transformations using lookup tables.\n\n**Purpose**\n\nLookups provide a way to translate values from one system to another. They transform\ninput values into output values using either static mapping tables or\ndynamic lookup caches.\n\n**Lookup mechanisms**\n\nThere are two distinct lookup mechanisms available:\n\n1. **Static Lookups**: Define a simple key-value map object and store it as part of your resource\n   - Best for: Small, fixed sets of values that rarely change\n   - Implementation: Configure the `map` object with input-to-output value mappings\n   - Example: Country codes, status values, simple translations\n\n2. **Dynamic Lookups**: Reference an existing 'Lookup Cache' resource in your Celigo account\n   - Best for: Large datasets, frequently changing values, or complex reference data\n   - Implementation: Configure `_lookupCacheId` to reference cached data maintained independently\n   - Example: Product catalogs, customer databases, pricing information\n\n**Property usage**\n\nThere are two mutually exclusive ways to configure lookups, depending on which mechanism you choose:\n\n1. **For Static Mappings**: Configure the `map` property with a direct key-value object\n   ```json\n   \"map\": {\"US\": \"United States\", \"CA\": \"Canada\"}\n   ```\n\n2. **For Dynamic Lookups**: Configure the following properties:\n   - `_lookupCacheId`: Reference to the lookup cache resource\n   - `extract`: JSON path to extract specific value from the returned lookup object\n\n**When to use**\n\nLookups are ideal for:\n\n1. **Value Translation**: Mapping codes or IDs to human-readable values\n\n2. **Data Enrichment**: Adding related information to records during processing\n\n3. **Normalization**: Ensuring consistent formatting of values across systems\n\n**Implementation details**\n\nLookups can be referenced in:\n\n1. **Field Mappings**: Direct use in field transformation configurations\n\n2. **Handlebars Templates**: Use within templates with the syntax:\n   ```\n   {{lookup 'lookupName' record.fieldName}}\n   ```\n\n**Example usage**\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"countryCodeToName\",\n    \"map\": {\n      \"US\": \"United States\",\n      \"CA\": \"Canada\",\n      \"UK\": \"United Kingdom\"\n    },\n    \"default\": \"Unknown Country\",\n    \"allowFailures\": true\n  },\n  {\n    \"name\": \"productDetails\",\n    \"_lookupCacheId\": \"60a2c4e6f321d800129a1a3c\",\n    \"extract\": \"$.details.price\",\n    \"allowFailures\": false\n  }\n]\n```\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for the lookup table within this configuration.\n\nThis name must be unique within the scope where the lookup is defined and is used to reference\nthe lookup in handlebars templates with the syntax {{lookup 'name' value}}.\n\nChoose descriptive names that indicate the transformation purpose, such as:\n- \"countryCodeToName\" for country code to full name conversion\n- \"statusMapping\" for status code translations\n- \"departmentCodes\" for department code to name mapping\n"},"map":{"type":["object","null"],"description":"The lookup mapping table as key-value pairs. The platform stores `null`\nhere on dynamic lookups, which resolve values at runtime instead of\nfrom a static table.\n\nThis object contains the input values as keys and their corresponding\noutput values. When a input value matches a key in this object,\nit will be replaced with the corresponding value.\n\nThe map should be kept to a reasonable size (typically under 100 entries)\nfor optimal performance. For larger mapping requirements, consider using\ndynamic lookups instead.\n\nMaps can include:\n- Simple code to name conversions: {\"US\": \"United States\"}\n- Status transformations: {\"A\": \"Active\", \"I\": \"Inactive\"}\n- ID to name mappings: {\"100\": \"Marketing\", \"200\": \"Sales\"}\n\nValues can be strings, numbers, or booleans, but all are stored as strings\nin the configuration.\n"},"_lookupCacheId":{"type":"string","description":"Reference to a LookupCache resource that contains the reference data for the lookup.\n\n**Purpose**\n\nThis field connects the lookup to an external data source that has been cached in the system.\nUnlike static lookups that use the `map` property, dynamic lookups can reference large datasets\nor frequently changing information without requiring constant updates to the integration.\n\n**Implementation details**\n\nThe LookupCache resource referenced by this ID contains:\n- The data records to be used as a reference source\n- Configuration for how the data should be indexed and accessed\n- Caching parameters to balance performance with data freshness\n\n**Usage patterns**\n\nCommonly used to reference:\n- Product catalogs or SKU databases\n- Customer or account information\n- Pricing tables or discount rules\n- Complex business logic lookup tables\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n","format":"objectid"},"extract":{"type":"string","description":"JSON path expression that extracts a specific value from the cached lookup object.\n\n**Purpose**\n\nWhen using dynamic lookups with a LookupCache, this JSON path identifies which field to extract\nfrom the cached object after it has been retrieved using the lookup key.\n\n**Implementation details**\n\n- Must use JSON path syntax (similar to mapping extract fields)\n- Operates on the cached object returned by the lookup operation\n- Examples:\n  - \"$.name\" - Extract the name field from the top level\n  - \"$.details.price\" - Extract a nested price field\n  - \"$.attributes[0].value\" - Extract a value from the first element of an array\n\n**Usage scenario**\n\nWhen a lookup cache contains complex objects:\n```json\n// Cache entry for key \"PROD-123\":\n{\n  \"id\": \"PROD-123\",\n  \"name\": \"Premium Widget\",\n  \"details\": {\n    \"price\": 99.99,\n    \"currency\": \"USD\",\n    \"inStock\": true\n  }\n}\n```\n\nSetting extract to \"$.details.price\" would return 99.99 as the lookup result.\n\nIf no extract is provided, the entire cached object is returned as the lookup result.\n"},"default":{"type":["string","null"],"description":"Default value to use when the source value is not found in the lookup map.\nThe platform stores `null` here when no default is configured.\n\nThis value is used as a fallback when:\n1. The source value doesn't match any key in the map\n2. allowFailures is true or omitted\n\nSetting an appropriate default helps prevent flow failures due to unexpected\nvalues and provides predictable behavior for edge cases.\n\nCommon default patterns include:\n- Descriptive unknowns: \"Unknown Country\", \"Unspecified Status\"\n- Original value indicators: \"{Original Value}\", \"No mapping found\"\n- Neutral values: \"Other\", \"N/A\", \"Miscellaneous\"\n\nA save refuses a default together with an explicit `allowFailures: false`\n(422 `Lookups with default set can not have allowFailures explicitly set to\nfalse.`): a lookup either fails a miss (`allowFailures: false`, no default) or\nfalls back to its default.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, missing lookup values will use the default value rather than causing an error.\n\n**Behavior control**\n\nThis field determines how the system handles source values that don't exist in the map:\n\n- true: Use the default value for missing mappings and continue processing\n- false: Treat missing mappings as errors, failing the record (`lookup_error` for a\n  static map, `lookupcache_evaluation_failed` for a lookup cache)\n- omitted or null: the record doesn't fail either. On an import a static lookup writes\n  its default if it has one, and a static or cache lookup without one leaves the\n  mapped field out of the output, with no error\n\n**Recommendation**\n\nSet this to true when:\n- New source values might appear over time\n- Data quality issues could introduce unexpected values\n- Processing should continue even with imperfect mapping\n\nSet this to false when:\n- Complete data accuracy is critical\n- All possible source values are known and controlled\n- Missing mappings indicate serious data problems that should be addressed\n\nA default raises no error: records that miss go out with the default value and\nnothing reports them. Use true only when that fallback is acceptable at the\ndestination, and set false explicitly when a missing value must stop the record.\n"}}}},"Form":{"type":"object","description":"Configuration for creating user-friendly settings forms that make it easier for less technical users\nto configure integration resources.\n\n**Settings form builder**\n\nThe Settings Form Builder allows you to create or edit user-friendly fields that prompt for text entry\nor selections that will be returned as settings applied to this resource. Your forms can include any\nfield types that you see elsewhere in integrator.io, such as:\n\n- Text fields\n- Dropdown selections\n- Checkboxes\n- Radio buttons\n- Date pickers\n- Multi-select fields\n- Search fields\n\nForm fields make it much easier for less technical users to work with your integration settings by:\n\n- Providing clear labels and help text\n- Enforcing validation rules\n- Offering pre-defined selection options\n- Grouping related settings logically\n- Supporting conditional visibility\n- Creating a consistent user experience\n","properties":{"form":{"type":"object","description":"Configuration that defines the structure, fields, and behavior of the settings form.\n\nThis object contains the complete definition of the form's layout, fields, validation rules,\nand interactive behaviors. The specific structure depends on the form complexity and can include\nfield definitions, sections, conditional display logic, and default values.\n\nThe form configuration is typically created and managed through the visual Form Builder interface\nrather than edited directly as JSON.\n","properties":{"fieldMap":{"type":"object","description":"A mapping of field identifiers to their configuration objects.\nEach key in this object represents a unique field ID, and the value contains\nall the configuration settings for that specific form field.\n","additionalProperties":{"type":"object","description":"Configuration for an individual form field.\n","properties":{"id":{"type":"string","description":"Unique identifier for this field within the form: layout containers and conditions\nrefer to the field by it. Use the same string as the field's fieldMap key and its `name`.\n"},"name":{"type":"string","description":"Key the field's value is saved under: one top-level key of the resource's `settings`,\nwhich the form also reads the value back from when it opens. Nested objects inside\n`settings` are written to it directly, not through a form field's `name`.\n"},"type":{"type":"string","description":"The type of form control to render for this field.\n","enum":["text","checkbox","radiogroup","relativeuri","editor","keyvalue","select","multiselect","toggle","datetime","date","exportSelect","staticMap"]},"label":{"type":"string","description":"Display label shown next to the field in the form.\n"},"description":{"type":"string","description":"Detailed explanation text that appears below the field, providing more context\nthan the label or helpText.\n"},"helpText":{"type":"string","description":"Explanatory text that appears when hovering over the help icon next to the field.\nUsed to provide additional guidance on how to use the field.\n"},"required":{"type":"boolean","description":"When true, the field must have a value before the form can be submitted. Only the form\nenforces it: an API write of `settings` is stored without the value.\n","default":false},"defaultValue":{"description":"Value the form shows while the resource's `settings` holds no value under the field's\n`name`.\n"},"multiline":{"type":"boolean","description":"For text fields, determines whether the input should be a multi-line text area\ninstead of a single-line input.\n","default":false},"rowsMax":{"type":"integer","description":"For multiline text fields, specifies the maximum number of visible rows.\n"},"inputType":{"type":"string","description":"For text fields, specifies the HTML input type attribute to apply additional\nvalidation or specialized input behavior.\n","enum":["text","number","email","password","tel","url"]},"delimiter":{"type":"string","description":"For text fields, specifies a character to use for splitting the input into an array.\nUsed for collecting multiple values in a single text field.\n"},"mode":{"type":"string","description":"For editor fields, specifies the type of content being edited for syntax highlighting.\n","enum":["json","xml","csv","text"]},"keyName":{"type":"string","description":"For keyvalue and staticMap fields, names the first column: each saved row holds that\ncolumn's value under this key. A keyvalue field without it uses `key`.\n"},"valueName":{"type":"string","description":"For keyvalue and staticMap fields, names the second column: each saved row holds that\ncolumn's value under this key. A keyvalue field without it uses `value`.\n"},"keyLabel":{"type":"string","description":"For staticMap fields, heading of the first column.\n"},"valueLabel":{"type":"string","description":"For staticMap fields, heading of the second column.\n"},"keyOptions":{"type":"array","description":"For staticMap fields, suggestions offered in the first column's cells. A cell still\naccepts a typed value.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"Display text for the suggestion.\n"},"value":{"type":"string","description":"Value saved when the suggestion is chosen.\n"}}}},"valueOptions":{"type":"array","description":"For staticMap fields, suggestions offered in the second column's cells. A cell still\naccepts a typed value.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"Display text for the suggestion.\n"},"value":{"type":"string","description":"Value saved when the suggestion is chosen.\n"}}}},"keyResource":{"type":"object","description":"For staticMap fields, fills the first column's suggestions from an export the form\ncarries inline in `virtual`, run the way an exportSelect's `resource` is. With\n`keyResource` or `valueResource` set, the form requires both columns in every row\nunless `keyValueRequired` is false.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline, shaped like an exportSelect's `resource.virtual`.\n","additionalProperties":true}}},"valueResource":{"type":"object","description":"For staticMap fields, fills the second column's suggestions from an export the form\ncarries inline in `virtual`, run the way an exportSelect's `resource` is.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline, shaped like an exportSelect's `resource.virtual`.\n","additionalProperties":true}}},"keyValueRequired":{"type":"boolean","description":"For staticMap fields with `keyResource` or `valueResource`, when false, saves rows\nthat fill only one column; by default the form drops them.\n","default":true},"resource":{"type":"object","description":"For exportSelect fields, the export whose records become the options. When the form\nopens, the UI previews the export in `virtual` (with `test.limit` 100 unless the export\nsets its own) and lists the preview's `data`: each record needs a `label` and a `value`\n(a string or number serves as both), so shape them with the export's `transform`. A\nsaved export can't be referenced, and an HTTP export that finds no records leaves the\nfield showing an error instead of options.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline: `adaptorType`, its adaptor block, and an optional\n`transform`. `_connectionId` defaults to the connection of the export or import the\nform belongs to; set it on any other resource's form.\n","additionalProperties":true}}},"multiselect":{"type":"boolean","description":"For exportSelect fields, when true, the user can choose several options and the value\nis saved as an array.\n","default":false},"supportHandlebars":{"type":"boolean","description":"For exportSelect fields, when true, renders the `resource.virtual` export as a Handlebars\ntemplate before each preview (all but its `transform`, `hooks`, and `filter`). On an\nintegration, flow group, flow, export, or import form, the form's current values are\nunder that form's scope name (`integration`, `flowGrouping`, `flow`, `export`, or\n`import`), beside the saved settings of the scopes above it, with no `settings.`\nprefix; on a connection or iClient form the template has no context. The options then\nload through `refreshOptionsOnChangesTo` or `typeAheadSearch`, or when the user\nrefreshes the field.\n","default":false},"refreshOptionsOnChangesTo":{"type":"array","items":{"type":"string"},"description":"For exportSelect fields with `supportHandlebars`, ids of the fields whose values the\ntemplated export uses: the options load once one of them has a value and reload whenever\none changes.\n"},"typeAheadSearch":{"type":"boolean","description":"For exportSelect fields, when true, re-runs the templated export as the user types, with\nthe typed text as this field's own value; an empty box clears the options, and typed\ntext is accepted as the value.\n","default":false},"showDelete":{"type":"boolean","description":"For keyvalue fields, determines whether to show a delete button for each key-value pair.\n"},"doNotAllowFutureDates":{"type":"boolean","description":"For date and datetime fields, restricts selection to dates not in the future.\n"},"skipTimezoneConversion":{"type":"boolean","description":"For datetime fields, prevents automatic timezone conversion of the date value.\n"},"options":{"type":"array","description":"For fields that present choices (select, multiselect, radiogroup, toggle), defines\nthe available options.\n","items":{"anyOf":[{"title":"Option group","type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"oneOf":[{"title":"String value","type":"string"},{"title":"Label-value pair","type":"object","properties":{"label":{"type":"string","description":"Display text for the option.\n"},"value":{"type":"string","description":"Value to store when this option is selected.\n"}}}]},"description":"Array of option values/labels to display in the selection control.\n"}}},{"title":"Label-value pair","type":"object","required":["label"],"properties":{"label":{"type":"string","description":"Display text for the option.\n"},"value":{"type":"string","description":"Value to store when this option is selected.\n"}}}]}},"visibleWhen":{"type":"array","description":"Conditional display rules that determine when this field should be visible.\nIf empty or not provided, the field is always visible.\n","items":{"type":"object","properties":{"field":{"type":"string","description":"The ID of another field whose value controls the visibility of this field. The\nsave doesn't check it: a rule naming an ID that isn't in the form counts as met,\nso the field stays visible.\n"},"is":{"type":"array","items":{"type":["string","boolean","number","null"]},"description":"Array of values - if the referenced field has any of these values,\nthis field will be visible. Values may be strings, booleans (for\ncheckbox/toggle fields), numbers (for numeric inputs), or null.\n"}}}}}}},"layout":{"type":"object","description":"Defines how the form fields are arranged and grouped in the UI.\nThe layout can organize fields into columns, sections, or other visual groupings.\n","properties":{"type":{"type":"string","description":"The type of layout to use for the form.\n","enum":["column","collapse","box","indent","tabWithoutSave","verticalTabWithoutSave"]},"fields":{"type":"array","items":{"type":"string"},"description":"Field IDs shown above the containers, without a heading.\n"},"containers":{"type":"array","description":"Array of container objects that group fields or contain nested containers.\nEach container can represent a column, box, indented section, or collapsible section.\n","items":{"type":"object","properties":{"type":{"type":"string","description":"The visual style of the container.\n","enum":["indent","box","collapse"]},"label":{"type":"string","description":"The heading text displayed for this container.\n"},"fields":{"type":"array","items":{"type":"string"},"description":"Array of field IDs that should be displayed in this container.\nEach ID must correspond to a key in the fieldMap object: the save doesn't check it,\nand an ID that doesn't renders nothing. A field the layout doesn't list isn't shown.\n"},"containers":{"type":"array","description":"Nested containers within this container. Allows for hierarchical organization\nof fields with different visual styles.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"The heading text displayed for this nested container.\n"},"fields":{"type":"array","items":{"type":"string"},"description":"Array of field IDs that should be displayed in this nested container.\n"}}}}}}}}}},"additionalProperties":true},"init":{"type":"object","description":"Configuration for custom JavaScript initialization that executes when the form is first loaded.\n\nThis object defines a JavaScript hook that prepares the form for use, sets initial field values,\nperforms validation, or otherwise customizes the form behavior before it is displayed to the user.\n\n**Function signature**\n\nThe initialization function is invoked with a single 'options' argument containing contextual information:\n```javascript\nfunction formInit(options) {\n  // Process options and return the form object\n  return options.resource.settingsForm.form;\n}\n```\n\n**Available context**\n\nThe 'options' argument provides access to:\n- `options.resource` - The current resource being configured\n- `options.parentResource` - The parent of the current resource\n- `options.grandparentResource` - The grandparent of the current resource\n- `options.license` - For integration apps, the license provisioned to the integration\n- `options.parentLicense` - For integration apps, the parent of the license\n- `options.sandbox` - Boolean flag indicating whether the script is running in a sandbox environment\n\n\n**Common uses**\n\n- Dynamically generate field options based on resource configuration\n- Pre-populate default values from related resources\n- Apply conditional logic that depends on resource properties\n- Add, remove, or modify form fields based on user permissions or account settings\n- Fetch external data to populate selection options\n- Implement complex validation rules that depend on resource context\n- Create branching form experiences based on user selections\n\n**Return value**\n\nThe function must return a valid form object that the UI can render.\nThrowing an exception will signal an error to the user.\n\nThe UI runs the function each time it opens the form and renders the returned form instead of\n`form`, then fills in the values from `settings`; a thrown error's message replaces the form.\n","properties":{"function":{"type":"string","description":"The name of the function to execute within the referenced script.\n\nThis property specifies which function to invoke from the script\nreferenced by _scriptId. The function will be called when the form\nis initialized and should handle any custom setup logic.\n\nThe function must follow the expected signature and return a valid form object.\n"},"_scriptId":{"type":"string","format":"objectId","description":"Reference to a predefined script resource containing the initialization function.\n\nThe referenced script should contain the function specified in the\n'function' property. On an integration, a flow group, a flow, an export, an import, or a\nconnection, an id that isn't a script in the same account (or, on an Integration App's\nresources, one the app shares) is refused with 422 `invalid_ref`. An iClient save\nstores the id without that check.\n"}}}}},"Settings":{"type":"object","description":"Configuration settings that can be accessed by hooks, filters, mappings and handlebars templates at runtime.\n\nIt enables customization of the resource's logic, allowing hooks, mappings, filters, and\nhandlebars to access and apply the settings at runtime.\n\n**Usage**\n\nThe settings object can store arbitrary JSON data that you want to save with the resource.\nWhile it's often populated through a form defined in the `settingsForm` field, you can also:\n\n- Directly provide JSON settings without using a form\n- Store configuration values used by hooks and templates\n- Create resource-specific constants and parameters\n- Maintain lookup tables or mapping structures\n- Define conditional logic parameters\n\n**Accessibility**\n\nSettings are available in:\n- All handlebars fields for building dynamic payloads\n- Field mapping expressions\n- JavaScript hooks via the options object\n- Filters and transformations\n\nAt runtime, the settings objects in the step's execution chain are gathered into a\nsingle `settings` context keyed by fixed scope keys. Flow runs populate the full\nchain: `settings.integration.*`, `settings.flowGrouping.*` (the flow group the flow\nbelongs to), `settings.flow.*`, `settings.connection.*`, `settings.iClient.*`, and\nthe running step's own scope — `settings.export.*` on exports/lookups,\n`settings.import.*` on imports (the key matches the step type). Steps executing\ninside a My API or a Tool receive only the step's own scope plus\n`settings.connection.*` / `settings.iClient.*`; the integration, flowGrouping, and\nflow scopes resolve empty there. The scope key is literal — resource display names\nand settingsForm section labels are never part of the path. The segments after the\nscope mirror the stored settings JSON key path exactly: a top-level field is\n`settings.<scope>.<fieldId>`, and nested objects add one segment per JSON key\n(e.g. `settings.flowGrouping.Customer.region` when the group's settings JSON nests\n`region` under `Customer`). References without a scope key\n(e.g. `{{settings.myField}}`) resolve to empty strings.\n\n**Best practices**\n\nFor non-technical users, create a custom form instead of editing the JSON directly.\nThis provides a user-friendly interface for updating settings without requiring JSON knowledge.\n","additionalProperties":true},"MockOutput":{"type":["object","null"],"description":"Sample data that simulates the output from an export for testing and configuration purposes.\n\nMock output allows you to configure and test flows without executing the actual export or\nwaiting for real-time data to arrive. This is particularly useful for:\n- Initial flow configuration and testing\n- Mapping development without requiring live data\n- Generating metadata for downstream flow steps\n- Creating realistic test scenarios\n- Documenting expected data structures\n\n**Structure**\n\nThe mock output must follow the integrator.io canonical format, which consists of a\n`page_of_records` array containing record objects. Each record object has a `record`\nproperty that contains the actual data fields.\n\n```json\n{\n  \"page_of_records\": [\n    {\n      \"record\": {\n        \"field1\": \"value1\",\n        \"field2\": \"value2\",\n        ...\n      }\n    },\n    ...\n  ]\n}\n```\n\n**Usage**\n\nIn a flow test run, and for an upstream lookup during a page-processor preview\n(`previewPageProcessor`), integrator.io substitutes this mock output for the export's live\nresponse instead of calling the source. A saved mock is not read anywhere else: a target\nlookup in that preview runs its live query, invoking the export\n(`POST /v1/exports/{_id}/invoke`) reads the source, and real runs ignore it. This allows you\nto:\n- Test mappings with representative data\n- Configure downstream flow steps without waiting for real data\n- Simulate various data scenarios\n\nWhere it is substituted, the records stand in for the RAW parsed response: the export's own\n`transform` and output `filter` still run on top of them, so downstream steps — and a\nlookup's results mapping — see the transformed, filtered records, not the shape stored here.\n\nIn a flow test run of an HTTP export with a saved mock (live probe, 2026-10-21), the export\nstill listed `request` and `response` stages: `request` held the request the export composes,\nand `response` held the mock under the source's URL, as did the test run's request log, which\nrecorded it as a 200. Read those stages as the substituted mock, not as a live call.\n\n`POST /v1/exports/preview` reads the `mockOutput` in its request body, not the saved one. For\nan export the preview supports (an HTTP export, for example), a body that carries it (as an\nexport read back with GET does) returns that snapshot as a lone `parse` stage without calling\nthe source; remove `mockOutput` from the body to preview live records.\n\n**Limitations**\n\n- Maximum of 10 records\n- Maximum size of 1 MB\n- Must follow the canonical format shown above\n\nMock output can be populated automatically from preview data (which is the parsed response,\nbefore `transform` / `filter`) or entered manually.\n","properties":{"page_of_records":{"type":"array","description":"Array of record objects in the integrator.io canonical format.\n\nEach item in this array represents one record that would be processed\nby the flow during execution.\n","items":{"type":"object","properties":{"record":{"type":"object","description":"Container for the actual record data fields.\n\nThe structure of this object will vary depending on the specific\nexport configuration and the source system's data structure.\n","additionalProperties":true}}}}}},"PreSave":{"type":"object","description":"Defines a JavaScript hook that executes before the resource is saved.\n\nThis hook allows for programmatic validation, transformation, or enrichment of the\nresource itself before it is persisted. It can be used to enforce business rules,\nset derived properties, or implement cross-field validations that can't be expressed\nthrough the standard UI.\n\n**Function signature**\n\nThe preSave function is invoked with a single 'options' argument containing:\n```javascript\nfunction preSave(options) {\n  // Process options and return the modified resource\n  return options.newResource;\n}\n```\n\n**Available context**\n\nThe 'options' argument provides access to:\n- `options.newResource` - The resource being saved (with pending changes)\n- `options.oldResource` - The previous version of the resource (before changes)\n- `options.sandbox` - Boolean flag indicating whether the script is running in a sandbox environment\n\n\n**Common uses**\n\n- Enforcing complex business rules across multiple fields\n- Automatically deriving field values based on other configuration\n- Performing validation that depends on external systems or data\n- Normalizing or standardizing configuration values\n- Adding computed or derived properties\n- Implementing versioning or change tracking\n- Dynamically looking up data using the Celigo API module to enrich configuration\n\n**Return value**\n\nThe function must return the newResource object (potentially modified) to be saved.\nThrowing an exception will prevent saving and signal an error to the user.\n","properties":{"function":{"type":"string","description":"The name of the function to execute within the referenced script.\n\nThis property specifies which function to invoke from the script\nreferenced by _scriptId. The function will be called just before\nthe resource is saved.\n\nThe function must follow the expected signature and return the resource object.\n"},"_scriptId":{"type":"string","format":"objectId","description":"Reference to a predefined script resource containing the preSave function.\n\nThe referenced script should contain the function specified in the\n'function' property. This script must be accessible within the user's account\nand have appropriate permissions.\n"}}},"ResourceResponse":{"type":"object","description":"Response","properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Unique identifier for the resource. Format is a 24-character hexadecimal string."},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was created. Set automatically and cannot be modified."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was last updated. Changes whenever any property is modified."},"deletedAt":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp when the resource was soft-deleted. When null or absent, the resource is active."}},"required":["_id"]},"IAResourceResponse":{"type":"object","description":"Integration app response fields for resources that are part of integration apps","properties":{"_integrationId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the specific integration instance that contains this resource.\n\nThis field is only populated for resources that are part of an integration app\ninstallation. It contains the unique identifier (_id) of the integration\nresource that was installed in the account.\n\nThe integration instance represents a specific installed instance of an\nintegration app, with its own configuration, settings, and runtime environment.\n\nThis reference enables:\n- Tracing the resource back to its parent integration instance\n- Permission and access control based on integration ownership\n- Lifecycle management (enabling/disabling, updating, or uninstalling)\n"},"_connectorId":{"type":"string","format":"objectId","readOnly":true,"description":"Integration App (connector) that defines this resource. Present only on resources an\nIntegration App owns. The app locks parts of these documents, and a PUT that changes a\nlocked field answers 200 but keeps the stored value, so read the document back to confirm\na change."}}},"AIDescription":{"type":"object","description":"AI-generated descriptions and documentation for the resource.\n\nThis object contains automatically generated content that helps users\nunderstand the purpose, behavior, and configuration of the resource without\nrequiring them to analyze the technical details. The AI-generated content\nis sanitized and safe for display in the UI.\n","properties":{"summary":{"type":["string","null"],"description":"Brief AI-generated summary of the resource's purpose and functionality.\n\nThis concise description provides a quick overview of what the resource does,\nwhat systems it interacts with, and its primary role in the integration.\nThe summary is suitable for display in list views, dashboards, and other\ncontexts where space is limited.\n\nMaximum length: 10KB\n"},"detailed":{"type":["string","null"],"description":"Comprehensive AI-generated description of the resource's functionality.\n\nThis detailed explanation covers the resource's purpose, configuration details,\ndata flow patterns, filtering logic, and other technical aspects. It provides\nin-depth information suitable for documentation, tooltips, or detailed views\nin the administration interface.\n\nThe content may include HTML formatting for improved readability.\n\nMaximum length: 10KB\n"},"generatedOn":{"type":["string","null"],"format":"date-time","description":"Timestamp indicating when the AI description was generated.\n\nThis field helps track the freshness of the AI-generated content and\ndetermine when it might need to be regenerated due to changes in the\nresource's configuration or behavior.\n\nThe timestamp is recorded in ISO 8601 format with UTC timezone (Z suffix).\n"}}},"APIM":{"type":"array","description":"Read-only field that stores information about the integration resources\npublished in the API Management (APIM) platform.\n\nThis field tracks the relationship between integrator.io resources and their\npublished counterparts in the APIM platform, which is\ntightly integrated with the Celigo UI. When resources are \"pushed\" to APIM,\nthis field is populated with the relevant identifiers and statuses.\n","items":{"type":"object","properties":{"apiId":{"type":"string","description":"Identifier for the API where this integrator.io resource is published in the APIM.\n\nThis is an APIM resource identifier (not prefixed with underscore like Celigo IDs)\nthat uniquely identifies the API in the API Management platform.\n"},"flowId":{"type":"string","description":"Identifier for the flow within the API where this integrator.io resource is linked.\n\nWhen an API has multiple integrator.io resources linked, each resource is associated\nwith a specific flow in the API, identified by this field. This is an APIM\nresource identifier.\n"},"status":{"type":"string","description":"Indicates the publishing stage of the integrator.io resource in APIM.\n\nPossible values:\n- 'oaspending': The resource is published but the OpenAPI Specification (OAS) is not\n  yet published. The apiId will be updated with the API ID created in APIM.\n- 'published': The OpenAPI Specification for the integrator.io resource has been\n  successfully uploaded to APIM.\n","enum":["oaspending","published"]}}}}}}}
````

## List exports

> Returns a list of all exports configured in the account.\
> If no exports exist in the account, a 204 response with no body will be returned.<br>

````json
{"openapi":"3.2.0","info":{"title":"Exports","version":"1.0.0"},"tags":[{"name":"Exports","description":"Exports retrieve data from source systems — on a schedule, in delta mode, in real time,\non demand, or as a file/blob transfer — package the results into ≤ 5-MB pages, and pass\neach page to downstream flow steps. Depending on configuration, an export surfaces in the\nFlow Builder as an export, a real-time listener, a file transfer, or a mid-flow lookup.\n\n## Export schema\n\n{% openapi-schemas spec=\"export\" schemas=\"Export\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"parameters":{"After":{"name":"after","in":"query","required":false,"description":"Opaque cursor for forward pagination. Pass the value from the `Link`\nresponse header (`rel=\"next\"`) to fetch the next page.","schema":{"type":"string"}},"Include":{"name":"include","in":"query","required":false,"description":"Comma-separated list of fields to project into each returned record.\nTriggers summary projection: the response contains a minimal identity\nset (`_id`, `name`, plus resource-specific fields) with the requested\nfields added on top. Supports dot notation for nested fields.\nMutually exclusive with `exclude`.","schema":{"type":"string"}},"Exclude":{"name":"exclude","in":"query","required":false,"description":"Comma-separated list of fields to strip from the default response.\nUnlike `include`, does not trigger summary projection — returns the\nfull record with the named fields removed. Protected identity fields\n(e.g. `name`) cannot be stripped. Mutually exclusive with `include`.","schema":{"type":"string"}}},"schemas":{"Export":{"type":"object","required":["_id","name","adaptorType","createdAt","lastModified"],"description":"Export object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ExportBase"},{"$ref":"#/components/schemas/ResourceResponse"},{"$ref":"#/components/schemas/IAResourceResponse"},{"type":"object","properties":{"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"apim":{"$ref":"#/components/schemas/APIM"},"apiIdentifier":{"type":"string","readOnly":true,"description":"API identifier assigned to this export."},"asynchronous":{"type":"boolean","readOnly":true,"description":"Server-managed execution-mode flag set on creation."},"__linkedLookupCacheIds":{"type":"array","readOnly":true,"description":"Lookup caches linked to this export, managed by the platform.","items":{"type":"string","format":"objectId"}},"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the source resource this export was created from."},"_templateId":{"type":"string","format":"objectId","readOnly":true,"description":"Template this export was created from."},"draft":{"type":"boolean","readOnly":true,"description":"When true, this export is in draft state and has not been confirmed."},"draftExpiresAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the draft version of this export expires."},"debugUntil":{"type":"string","format":"date-time","readOnly":true,"description":"Turns on step debug capture until this time: while it lies in the future, every\nrequest this export sends is recorded in the step's debug log\n(`GET /v1/{flows|apis|tools}/{containerId}/{_id}/requests`). Read-only on PUT —\nwritten only by `PATCH /v1/exports/{_id}` on `/debugUntil`, at most 3600 seconds\nahead (`422` `update_not_allowed` beyond that); `remove` stops capture."}}}]},"ExportBase":{"type":"object","description":"Writable export fields shared by the request and response schemas.","properties":{"name":{"type":"string","maxLength":100,"description":"Display name for the export, shown in the flow builder, job history, and error logs.\nDescriptive, unique names indicating the data source and purpose make large accounts easier to manage."},"description":{"type":["string","null"],"description":"Free-text summary of what the export retrieves and why. Shown in the UI and available\nto AI agents for context; has no effect on execution.","maxLength":5120},"_connectionId":{"format":"objectId","type":["string","null"],"description":"Connection this export uses to reach the source system. The connection's type must be\ncompatible with the export's `adaptorType` (e.g. an `HTTPExport` needs an `http` connection;\n`NetSuiteExport` and `NetSuiteHTTPExport` both need a `netsuite` connection).\nServer-required unless `type` is `webhook` or `simple` (those receive data instead of\nfetching it) or `adaptorType` is `TopicExport` (Event Streams listeners are delivered\nby the platform itself and must omit it). A `WebhookExport` saved without `type: webhook`\nis refused with 422 \"_connectionId is required unless type is [webhook|simple]\"; a webhook\nexport that does carry a connection saves and updates normally."},"adaptorType":{"type":"string","description":"Selects the adaptor technology that executes this export, which determines the compatible\nconnection types and which adaptor-specific configuration object must also be supplied\n(e.g. set `salesforce` when using `SalesforceExport`).","enum":["HTTPExport","FTPExport","AS2Export","S3Export","NetSuiteExport","NetSuiteHTTPExport","SalesforceExport","JDBCExport","RDBMSExport","MongodbExport","DynamodbExport","WrapperExport","SimpleExport","WebhookExport","FileSystemExport","TopicExport","RESTExport"]},"nsDomainType":{"type":"string","enum":["suitetalk","restlet"],"description":"Selects which NetSuite REST host a `NetSuiteHTTPExport` calls. The server derives the\nfull base URL from the connection's NetSuite account\n(`https://<account>.suitetalk.api.netsuite.com` or `https://<account>.restlets.api.netsuite.com`)\nand signs each request with the connection's token-based credentials, so `http.relativeURI`\ncarries the complete path starting at `/services/rest/...` or `/app/site/hosting/restlet.nl`.\nIgnored on other adaptor types."},"type":{"type":["string","null"],"description":"Operational mode of the export. When omitted, the export retrieves all available records —\nthe standard batch mode, which is also the right choice for parsing structured files\n(CSV/XML/JSON) into records. Most modes take their matching configuration object (e.g. a\n`webhook` object when `type` is `webhook`); an HTTP or SQL `delta` export saves without a\n`delta` object because its request carries `{{lastExportDateTime}}`, though\n`delta.startDate` sets its first window. It still needs `type: delta`: on an export\nwith no `type`, `{{lastExportDateTime}}` and `{{currentExportDateTime}}` render\nempty on every run, so a request that carries them goes out with blank values each time\n(live-verified 2026-10-22). Use `blob` only to transfer raw files without parsing their\ncontents.","enum":["webhook","test","delta","once","tranlinedelta","simple","blob","distributed","stream","all"]},"pageSize":{"type":["integer","null"],"description":"Number of records per page streamed to downstream flow steps when the export is the\nflow's source step. Pages are additionally capped at 5 MB regardless of record count, so\nvery large records may produce smaller pages. The server does not validate the value.\nA lookup (`isLookup: true`) runs to completion and hands every result to the incoming\nrecord as one response, capped at 5 MB in total, so the value does not change how many\nresults a lookup returns; a result set larger than that belongs in a standalone export\nstep. In a test run or preview the value also caps the records fetched (default 20).\nOn a webhook listener the records of each incoming request are paged separately, before\nany hook or downstream step runs.","default":20},"dataURITemplate":{"type":"string","description":"Handlebars template that builds a link back to each record in the source application's UI\n(e.g. `https://my.salesforce.com/lightning/r/Contact/{{record.Id}}/view`). The resolved\nURL is stored with error records in job history so users can jump straight to the record."},"traceKeyTemplate":{"type":["string","null"],"description":"Handlebars template that overrides how each record's unique trace key is generated, used\nto track records through the flow and match retries to prior errors (e.g.\n`{{join \"_\" record.customerId record.orderId}}`). When omitted, the system picks a unique\nfield automatically from the record's top level, by exact field name (`GET\n/v1/flows/{_id}/traceKey` serves the pattern). The rendered value of a template is stamped\non each record as `io_custom_trace_key` and travels downstream with it. If a transform\nreshapes the data first, omit the `record.` prefix. Trace keys are capped at 256 characters;\na longer key is stored truncated from the middle, keeping the beginning and end of the\nvalue. Downstream imports use the trace key as their default concurrency lock key (a blank\nkey means no lock); it never de-duplicates — a record emitted twice with the same key is\nprocessed twice."},"skipRetries":{"type":"boolean","description":"When true, the platform does not retain the source data needed to retry failed records,\nreducing stored data at the cost of being unable to reprocess errors from this export.\nShown as \"Do not store retry data\" in the UI. Defaults to false."},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"isLookup":{"type":"boolean","description":"When true, the export runs as a mid-flow lookup: it executes once per incoming record,\nusing the input record's fields to parameterize the request, and passes the results to\nsubsequent steps. When false, the export runs as a standalone data source. A lookup's\nresults are not paged through the flow: every page the adaptor fetches is merged into one\nresponse for the record, up to 5 MB, so `pageSize` does not limit or extend what a lookup\nreturns. A lookup that must return more than that is better modeled as a standalone\nexport step."},"groupByFields":{"$ref":"#/components/schemas/GroupBy"},"delta":{"$ref":"#/components/schemas/Delta"},"test":{"$ref":"#/components/schemas/Test"},"once":{"$ref":"#/components/schemas/Once"},"webhook":{"$ref":"#/components/schemas/Webhook"},"simple":{"$ref":"#/components/schemas/Simple"},"distributed":{"$ref":"#/components/schemas/Distributed"},"cdc":{"$ref":"#/components/schemas/Cdc"},"topic":{"$ref":"#/components/schemas/Topic"},"filesystem":{"$ref":"#/components/schemas/FileSystem"},"http":{"$ref":"#/components/schemas/Http"},"file":{"$ref":"#/components/schemas/File"},"salesforce":{"$ref":"#/components/schemas/Salesforce"},"as2":{"$ref":"#/components/schemas/AS2"},"dynamodb":{"$ref":"#/components/schemas/DynamoDB"},"ftp":{"$ref":"#/components/schemas/FTP"},"jdbc":{"$ref":"#/components/schemas/JDBC"},"mongodb":{"$ref":"#/components/schemas/MongoDB"},"netsuite":{"$ref":"#/components/schemas/NetSuite"},"rdbms":{"$ref":"#/components/schemas/RDBMS"},"s3":{"$ref":"#/components/schemas/S3"},"wrapper":{"$ref":"#/components/schemas/Wrapper"},"parsers":{"$ref":"#/components/schemas/Parsers"},"filter":{"description":"Filter applied to each record after the export's transform and before its preSavePage\nhook. Records that match continue through the flow; records that don't are silently\ndropped. Filter expressions reference the transformed record's fields.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"inputFilter":{"description":"Filter applied to incoming records before a lookup export queries the external system.\nOnly matching input records trigger lookup calls; non-matching records pass through the\nstep unenriched, which cuts unnecessary API traffic. Only relevant when `isLookup` is true.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"mappings":{"description":"Field mappings applied to each incoming record before a lookup HTTP request is made,\nreshaping the upstream record into the structure the lookup target API expects. Only\nsupported when `isLookup` is true and `adaptorType` is `HTTPExport` or\n`NetSuiteHTTPExport`; do not set it on source exports or other lookup adaptors.","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"transform":{"description":"Transformation that reshapes records before they leave this step — source exports\ntransform the retrieved records, lookup exports transform the lookup results. Commonly\nused to flatten nested lookup responses (e.g. `results[0].id` → `id`); merging results\nback into source records is handled separately by the flow's response mapping.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the export lifecycle for\ntransformations, validation, and filtering beyond what configuration alone can express.\nA hook name not listed here (an import's `preMap`, for example) is dropped on save\nwithout an error, so read `hooks` back after saving.","properties":{"preSavePage":{"type":"object","description":"Hook that runs on each page of records after the export's transform and output\nfilter, before the page is sent to downstream steps; commonly used to flatten\nstructures, drop unwanted records, or add computed fields. It receives the page as\n`options.data` (with `errors`, `pageIndex`, `configuration` and `settings`) and returns\n`{data, errors, abort, newErrorsAndRetryData}`. Return that object, not the array: a\nhook that returns `options.data` itself, or a filtered copy, drops the page without an\nerror, and the run completes with 0 records, 0 errors and 0 pages. An exception thrown\nhere fails every record on that page, each with its own retry data, and later pages\nstill run.","properties":{"function":{"type":"string","description":"Function to invoke within the referenced script."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`. Must be a script in this\naccount: any other id fails the save with 422 (\"Only a script owned by the user or\na script shared by the Integration App can be used at hooks.preSavePage._scriptId.\")."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments."},"configuration":{"type":"object","description":"Static parameters passed to the hook function at runtime, letting one script be\nreused across exports with different settings."}}},"preParse":{"type":"object","description":"Hook that runs on file-based exports after the file is downloaded and before it is\nparsed: it receives the raw file text as `preParseData` together with `fileMeta` and\nreturns both, modified or not, for the configured parser to read. Used to normalize a\nnon-standard file — fix a header row, strip a preamble, convert a format — before\nparsing; an exception thrown here fails the run.","properties":{"function":{"type":"string","description":"Function to invoke within the referenced script."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments."},"configuration":{"type":"object","description":"Static parameters passed to the hook function at runtime, letting one script be\nreused across exports with different settings."}}}}},"settingsForm":{"$ref":"#/components/schemas/Form"},"settings":{"$ref":"#/components/schemas/Settings"},"mockOutput":{"$ref":"#/components/schemas/MockOutput"},"_ediProfileId":{"type":"string","format":"objectId","description":"EDI profile this export uses to parse incoming X12 or EDIFACT documents into structured\nJSON — it supplies the envelope qualifiers, delimiters, version, and validation rules.\nSet it when the export parses or validates EDI files (a save with a `delimited/x12` file\ndefinition at `version` `\"2\"` and no profile fails with 422 `missing_ediProfile`); omit\nit otherwise. Accepted on the file-reading exports — FTP, AS2, S3 and HTTP file mode\n(`http.type: \"file\"`)."},"_postParseListenerId":{"type":"string","format":"objectId","description":"Webhook export invoked once per file after EDI parsing, on both success and failure\n(errors are included in the payload when parsing fails). Primarily used to send\nfunctional acknowledgements (997/999) to trading partners. Accepted on the file-reading\nexports that parse EDI — FTP, AS2, S3 and HTTP file mode (`http.type: \"file\"`)."},"externalId":{"type":["string","null"],"description":"External identifier for correlating the export with a record in another system."},"_integrationId":{"type":["string","null"],"format":"objectId","description":"Not stored on an export: a value sent on create or on a full update is dropped, and a GET\ndoesn't return the field. An export isn't owned by an integration; to find the flows and\nother resources that use it, call `GET /v1/exports/{_id}/dependencies`."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this export was created from, set when the export is part of an installed\nintegration app. The app's install sets it: a create that sends it is refused with 403\n(\"Cannot create an Integration App resource\")."},"unencrypted":{"type":"object","description":"Custom configuration values stored without encryption and returned in API responses."},"useTechAdaptorForm":{"type":"boolean","description":"When true, the UI presents the full technical adaptor form for this export instead of\nthe simplified assistant form."},"rawData":{"type":"string","description":"Key referencing the raw sample payload captured for preview and testing."},"preSave":{"$ref":"#/components/schemas/PreSave"},"assistant":{"type":"string","description":"Identifier for the connector assistant used to configure this export. For the cloud\nfile providers it also selects the provider-specific location rules —\n`azuredatalakestoragegen2` (container in `file.directory.name`, path in\n`http.relativeURI`) and `googlecloudstorage` (bucket in `file.directory.storageRootId`,\nprefix in `file.directory.id`)."},"assistantMetadata":{"type":["object","string"],"additionalProperties":true,"description":"Metadata associated with the connector assistant configuration."},"sampleData":{"type":["string","object","array","null"],"description":"Sample data payload used for previewing and testing the export."},"sampleHeaders":{"type":"array","description":"Sample HTTP headers used for previewing and testing the export.","items":{"type":"object","properties":{"name":{"type":"string","description":"Header name."},"value":{"type":"string","description":"Header value."}}}},"sampleQueryParams":{"type":"array","description":"Sample query parameters used for previewing and testing the export.","items":{"type":"object","properties":{"name":{"type":"string","description":"Query parameter name."},"value":{"type":"string","description":"Query parameter value."}}}}}},"OneToMany":{"type":"boolean","description":"When true, the step runs once per child record instead of once per incoming record.\n`pathToMany` names the array field that holds the children inside an object record; when\nthe incoming record is itself an array (grouped or row-based data), leave `pathToMany` blank\nand each element becomes a record. The fan-out is scoped to this step: afterwards the children\nare re-joined into the record's original shape — the object with its array, or the array of rows —\ncarrying any response-mapping enrichment, and that re-joined record is what the next step\nreceives. Not for locating records in an export's HTTP response; use\n`http.response.resourcePath` for that. Applies to steps that receive a record — imports and\nlookups (an export referenced as a page processor of a flow, API, or Tool). A standalone export\n(a flow's page generator) has no incoming record, so the fields are saved but have no effect on\nthe records it produces; to emit one record per array element from a standalone export, use a\n`hooks.preSavePage` script, or for a file-definition export make the repeating segment the\nrecord boundary in the definition's rules.\n","default":false},"PathToMany":{"type":"string","description":"Path to the array of child records inside an object record when `oneToMany` is true, in dot\nnotation (`items`, `lines.lineItems`). Leave blank when the incoming record is itself an array\n(grouped or row-based data) — each element is then a child record. A path that does not resolve\nto an array processes zero records and reports success. Read only on steps that receive a\nrecord (imports, lookup exports); on a standalone export it is saved and ignored — see `oneToMany`.\n"},"GroupBy":{"type":"array","description":"Specifies which fields to use for grouping records in the export results. When configured, records with\nthe same values in these fields will be grouped together and treated as a single record by downstream\nsteps in your flow.\n\nFor example:\n- Group sales orders by customer ID to process all orders for each customer together\n- Group journal entries by accounting period to consolidate related transactions\n- Group inventory items by location to process inventory by warehouse\n\nWhen grouping is used, the export's page size determines the maximum number of groups per page, not individual\nrecords. Note that effective grouping typically requires that records with the same group field values appear\ntogether in the export data.\n\nNot a webhook listener feature: on a `webhook` export the platform applies it only within each page, after\nthe request has been paged, and only to adjacent rows — the field is not offered for listeners in the UI.\n","items":{"type":"string"}},"Delta":{"type":"object","description":"Configures incremental exports that retrieve only records created or modified since the\nflow's delta checkpoint; omit it for other export types. Adaptors that compare a record field\nname it in `dateField`, while HTTP exports and SQL queries carry {{lastExportDateTime}} in the\nrequest and save without a delta object. Each run's window runs from {{lastExportDateTime}}\n(the checkpoint) to {{currentExportDateTime}} (the time the run started). A first run is not a\nfull export: with no `startDate`, its {{lastExportDateTime}} is the run's own timestamp, so it\npicks up no earlier changes. The checkpoint moves to a run's window end only when the run\nexports records, so a run that exports nothing leaves it where it was, and after a failed run\nthe next run reuses the last checkpoint. On success, when `POST /v1/flows/{_id}/run` sends\n`export.endDate`, the flow's checkpoint is left at that value, so an end date in the past\nmoves the next run's lower bound back.","properties":{"dateField":{"type":"string","description":"Record timestamp field(s) compared against the last successful run time to identify\nchanged records. Accepts a single field or multiple comma-separated fields, processed\nsequentially — useful when different operations update different timestamp fields.\nIf the flow's own downstream steps update the exported records, add export criteria\nthat exclude already-processed records (or use a creation-time field for process-once\nflows) so each run doesn't re-export what the previous run wrote. Not supported on\nHTTP exports: embed {{lastExportDateTime}} in the relativeURI or body instead; an HTTP\nexport saved with dateField is refused with 422 \"Unsupported dateField in\nexport.delta.dateField\". For Salesforce this\nfield is required and defaults to the standard timestamp fields (LastModifiedDate,\nCreatedDate, SystemModstamp, LastActivityDate, LastViewedDate, LastReferencedDate) plus\nany custom timestamp fields."},"dateFormat":{"type":"string","description":"Moment.js format string applied to both delta variables, {{lastExportDateTime}} and\n{{currentExportDateTime}}, in HTTP requests and SQL queries alike. Leave unset unless the\nsource system requires a non-ISO8601 format; by default both render as ISO 8601 UTC with\nmilliseconds. Date-only formats truncate the time portion, widening the filter window."},"lagOffset":{"type":"integer","description":"Buffer in milliseconds subtracted from the last successful run timestamp, creating an\noverlapping window that catches records still propagating when the previous run executed.\nSet it when records created or modified near the run time are occasionally skipped due to\nreplication or indexing delays. Keep it as low as possible — larger values reprocess\nredundant records. A negative value shifts the window forward (look-ahead) instead of back."},"startDate":{"type":["string","null"],"format":"date-time","description":"Lower-bound cutoff for the first run. Omitting it doesn't export history: the first run\nthen starts at its own timestamp, an empty window. Set it to the earliest change the first\nrun should pick up. Later runs start from the flow's checkpoint, the end of the last run\nthat exported records."}}},"Test":{"type":"object","description":"Configures test exports that cap the number of records retrieved, for safely developing and\nvalidating against small data samples. Required when the export's type is \"test\"; omit for\nother export types. A test export behaves like a standard export in every other way (filters,\npagination, processing) and stores no state between runs.","properties":{"limit":{"type":"integer","default":1,"description":"Caps the total records a test run processes, counted on top-level records before\noneToMany processing; the maximum exists to prevent accidentally processing large\ndatasets during development. The cap applies across pages — processing stops once the\nlimit is reached regardless of pageSize. When transitioning to production, leave this in\nplace and change the export's type field; the limit then no longer applies.","minimum":1,"maximum":100}}},"Once":{"type":"object","description":"Configures flag-based exports that skip records an earlier run already processed. Required\nwhen the export's type is \"once\"; omit for other export types. Each run retrieves records\nwhere the tracking boolean field is false, then sets that field to true after successful\nprocessing so later runs skip them; if a run fails, the flags are left unchanged and the\nrecords are retried automatically on the next run.","properties":{"booleanField":{"type":"string","description":"API field name of the boolean/checkbox in the source system that tracks processed\nrecords: the export selects only records where this field is false, then sets it to true\nin batches after each successfully processed page. The field must be writeable by the\nexport's connection. Ensure no other process updates the same field — use a separate\nflag per export process — or records may be skipped unexpectedly."}}},"Webhook":{"type":"object","description":"Configuration for webhook listeners that receive data through incoming HTTP requests. Required when the export's `type` is `webhook`; omit for other export types. Senders POST to the export's listener URL, `https://api.integrator.io/v1/exports/{_id}/data` (or the same path on the account's regional API host); the platform validates each request using the configured `verify` method, passes the payload to subsequent flow steps, and returns a configurable HTTP response to the caller. A request is answered 204 by default when accepted, 401 when it fails verification, and 403 `unable_to_run_flows` while every flow the export feeds is disabled; a refused request is not queued, so the sender must resend it.","properties":{"provider":{"type":"string","description":"Source application sending the webhook, used to pre-configure security settings and payload parsing for that platform. Use `custom` (the default when omitted) for unlisted providers or when you need full manual control over the security configuration. Provider-specific choices may require credentials (tokens, keys) mandated by that platform.","enum":["github","shopify","travis","travis-org","slack","dropbox","onfleet","helpscout","errorception","box","stripe","aha","jira","pagerduty","postmark","mailchimp","intercom","activecampaign","segment","recurly","shipwire","surveymonkey","parseur","mailparser-io","hubspot","integrator-extension","custom","sapariba","happyreturns","typeform"]},"verify":{"type":"string","description":"Verification method applied to every incoming request before processing; required for all webhook exports. Each method needs companion fields: `hmac` requires `key`, `algorithm`, `encoding`, and `header` (except on connector-backed webhooks, where the connector definition supplies them); `token` requires `token`, with `tokenLocation` defaulting to `body` and selecting which location-specific field applies; `basic` requires `username` and `password`; `secret_url` requires `token`. Prefer `hmac` when the source system supports it — it is the most secure option; `secret_url` relies only on URL obscurity and suits non-sensitive data or testing.","enum":["token","hmac","basic","secret_url"]},"token":{"type":"string","description":"Shared secret used when `verify` is `token` or `secret_url`. For `token` verification, the value found at `path` in each request must exactly match (case- and whitespace-sensitive) or the request is rejected with a 401. For `secret_url`, the token is embedded in the webhook URL to create a hard-to-guess endpoint — generate a random, high-entropy value and treat it as a sensitive credential."},"algorithm":{"type":"string","description":"Hashing algorithm used to validate HMAC signatures when `verify` is `hmac`. Must match the algorithm the webhook sender uses — a mismatch causes every request to be rejected. Use `sha256` unless the provider explicitly requires another value.","enum":["sha1","sha256","sha384","sha512"]},"encoding":{"type":"string","description":"Encoding of the HMAC signature value when `verify` is `hmac`. Must match the encoding the webhook sender uses — a mismatch causes requests to be rejected with a 401 even when the signature is otherwise correct. With `provider: custom`, `hex` is compared as lowercase hex: an uppercase hex signature is rejected.","enum":["hex","base64"]},"key":{"type":"string","description":"Secret used to validate signatures when `verify` is `hmac`. The system computes a signature of the request body using this key and the configured `algorithm`, then compares it with the signature sent in the request header named by `header`. Treat it as a highly sensitive credential — never expose it in repositories or logs."},"header":{"type":"string","description":"Name of the request header that carries the HMAC signature when `verify` is `hmac`. Must match the header name the webhook sender uses (header names are case-insensitive); requests without this header are rejected with a 401. With `provider: custom` the header value must be the bare digest in the configured `encoding`: a value with a `sha256=` prefix is rejected with a 401."},"tokenLocation":{"type":"string","enum":["body","header","queryParam"],"default":"body","description":"Where the verification token is found when `verify` is `token`. Each location uses a different companion field: `body` uses `path`, `header` uses `tokenHeaderName` (and `tokenHeaderScheme`), `queryParam` uses `tokenQueryParamName`."},"path":{"type":"string","description":"JSON path into the request body holding the verification token when `verify` is `token` and `tokenLocation` is `body` (e.g. `meta.token`). The value at this path must exactly match `token` or the request is rejected."},"tokenHeaderName":{"type":"string","description":"Request header carrying the verification token when `verify` is `token` and `tokenLocation` is `header`."},"tokenHeaderScheme":{"type":"string","enum":["bearer","custom","none"],"default":"bearer","description":"Scheme prefixing the token in the header when `tokenLocation` is `header`."},"customTokenScheme":{"type":"string","description":"Custom header scheme prefix used when `tokenHeaderScheme` is `custom`."},"tokenQueryParamName":{"type":"string","description":"Query parameter carrying the verification token when `verify` is `token` and `tokenLocation` is `queryParam`."},"_httpConnectorId":{"type":"string","format":"objectId","description":"HTTP connector backing this listener when the webhook is provided by an assistant/connector (e.g. Slack); set by the platform for connector-backed listeners."},"requestMediaType":{"type":"string","enum":["json","xml","csv","urlencoded","plaintext"],"description":"Overrides how the incoming request body is parsed when the provider sends a non-standard content type. When omitted, the body is parsed by its Content-Type header."},"pathToRecords":{"type":"string","description":"JSON path into an object payload to the array whose elements become the records. When omitted, a top-level JSON array is emitted as one record per element and an object payload is emitted as a single record."},"includeParentData":{"type":"boolean","description":"When true and `pathToRecords` is set, each emitted record also carries the surrounding parent fields from the payload."},"username":{"type":"string","description":"Username half of the credentials when `verify` is `basic`. Incoming requests must include an `Authorization: Basic` header carrying the base64-encoded `username:password` pair. Use only over HTTPS to prevent credential interception."},"password":{"type":"string","description":"Password half of the credentials when `verify` is `basic`, validated together with `username` from the request's `Authorization` header. Use a strong, unique value and treat it as a sensitive credential."},"successStatusCode":{"type":"integer","description":"HTTP status code returned to the caller after successful processing; must be a valid 2xx code. The default 204 returns no response body and causes `successBody` to be ignored — set 200 or 202 when the caller needs a response body or expects a specific code.","default":204},"successBody":{"type":"string","description":"Response body returned to the caller after successful processing; ignored when `successStatusCode` is 204. Content type is set by `successMediaType`, and the value can be static text or structured JSON/XML, including handlebars expressions for dynamic values."},"successMediaType":{"type":"string","description":"Sets the Content-Type header on successful webhook responses. Only takes effect when a `successBody` is returned (a status code other than 204), and must match the actual format of that body.","default":"json","enum":["json","xml","plaintext"]},"successResponseHeaders":{"type":"array","description":"Custom headers added to successful webhook responses — for example CORS headers or correlation IDs. Headers defined here take precedence over automatically set headers such as Content-Type, and values support handlebars expressions for dynamic content.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the header to set on successful webhook responses; headers defined here take precedence over automatically set headers such as Content-Type."},"value":{"type":"string","description":"Value sent for the header; supports handlebars expressions for dynamic content."}}}},"challengeResponseHeaders":{"type":"array","description":"Custom headers returned for webhook subscription verification (challenge) requests, which providers send before delivering real events. Required header values vary by provider — consult the provider's documentation, since incorrect challenge headers can prevent the subscription from being verified.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the header to set on challenge (subscription verification) responses."},"value":{"type":"string","description":"Value sent for the header; consult the provider's documentation for required challenge header values."}}}},"challengeSuccessBody":{"type":"string","description":"Response body returned for webhook subscription verification (challenge) requests. Many providers require echoing back a challenge value from the request, which handlebars expressions can access — for example `{{hub.challenge}}` (Facebook/Instagram) or `{\"challenge\":\"{{challenge}}\"}` (Slack). An incorrect challenge response prevents the subscription from being verified."},"challengeSuccessStatusCode":{"type":"integer","description":"HTTP status code returned for webhook subscription verification (challenge) requests. Most providers expect the default 200; change it only when the provider's verification explicitly requires a different code.","default":200},"challengeSuccessMediaType":{"type":"string","description":"Sets the Content-Type header on challenge responses. Must match both the format of `challengeSuccessBody` and the provider's requirements — for example, Slack expects `json` while Facebook/Instagram verification expects `plaintext`.","default":"json","enum":["json","xml","plaintext"]}},"if":{"properties":{"verify":{"const":"hmac"}},"required":["verify"],"not":{"required":["_httpConnectorId"]}},"then":{"required":["key","algorithm","encoding","header"]},"else":{"if":{"properties":{"verify":{"const":"token"}},"required":["verify"]},"then":{"required":["token"],"if":{"properties":{"tokenLocation":{"const":"body"}},"required":["tokenLocation"]},"then":{"required":["path"]},"else":{"if":{"properties":{"tokenLocation":{"const":"header"}},"required":["tokenLocation"]},"then":{"required":["tokenHeaderName"]},"else":{"if":{"properties":{"tokenLocation":{"const":"queryParam"}},"required":["tokenLocation"]},"then":{"required":["tokenQueryParamName"]},"else":{"required":["path"]}}}},"else":{"if":{"properties":{"verify":{"const":"basic"}},"required":["verify"]},"then":{"required":["username","password"]},"else":{"if":{"properties":{"verify":{"const":"secret_url"}},"required":["verify"]},"then":{"required":["token"]}}}}},"Simple":{"type":"object","description":"Configuration for `simple` (data-loader) exports — exports that accept manually uploaded\nfiles through the data-loader UI instead of fetching data from a connection. Relevant only\nwhen the export's `type` is `simple`; the uploaded content is parsed using the same file\nsettings as file-based exports.","properties":{"file":{"$ref":"#/components/schemas/File"}}},"File":{"type":"object","description":"Controls how files are parsed, filtered, and processed across all file-based exports\n(FTP/SFTP, Amazon S3, simple file uploads, and other file sources). The type field selects\nthe file format and determines which format-specific object (csv, json, xlsx, xml, or\nfileDefinition) must be configured; the output field selects whether files are parsed into\nrecords, transferred as blobs, or listed as metadata only. The filter object selectively\nskips files before processing.","properties":{"encoding":{"type":"string","description":"Character encoding used to read and parse file content. If the encoding is unknown, try\nutf8 first (the default), then win1252 for Western-language files with garbled\ncharacters; consider the geographic origin of the data when selecting.","enum":["utf8","win1252","utf-16le","gb18030","macroman","iso88591","shiftjis"]},"type":{"type":"string","description":"Format of the files being processed; determines which format-specific configuration\nobject (csv, json, xlsx, xml, or fileDefinition) must be populated — other format\nobjects are ignored. Required for all file-based exports except blob exports (export\ntype \"blob\" or output \"blobKeys\").","enum":["csv","json","xlsx","xml","filedefinition"]},"output":{"type":"string","description":"Processing mode for retrieved files: parse contents into records, transfer files as\nunparsed blobs, or return only file metadata. Determines what data is passed to\nsubsequent flow steps.","enum":["records","metadata","blobKeys"]},"skipDelete":{"type":"boolean","default":false,"description":"When true, source files remain on the file server after processing; when false (the\ndefault), files are deleted after successful processing. Files that fail processing are\nnever deleted, regardless of this setting. Enable retention when files must be processed\nby other flows or systems, or kept for compliance."},"compressionFormat":{"type":"string","description":"Compression format of incoming files, which are decompressed before any other processing\n(parsing, filtering). Set this only when the source always delivers compressed files —\nif a file marked as compressed is not actually compressed, processing fails. Leave unset\nwhen files arrive uncompressed or only sometimes compressed.","enum":["gzip","zip"]},"purgeInternalBackup":{"type":"boolean","description":"When true, Celigo keeps no internal backup copies of files processed by this export;\nwhen false (the default), copies are retained for your account's retention period and\nare available for reprocessing or troubleshooting. Applies only to this export and only\nto Celigo's internal copies — source files are governed by skipDelete. Enable for highly\nsensitive data or zero-retention policies; without backups, recovery may require\nre-obtaining files from the source system."},"decrypt":{"type":"string","description":"Decryption applied to incoming files before any other processing; decryption runs before\ndecompression, and a decryption failure fails the file's processing entirely. The\nconnection must already be configured with the private key (and passphrase, if\napplicable) matching the public key used to encrypt the files. Only PGP/GPG encryption\nis currently supported.","enum":["pgp"]},"batchSize":{"type":["integer","null"],"description":"Number of files retrieved per batch; if a batch fails, the whole batch is retried\n(stored as `null` when not configured). Use\nlower values (10-50) for large files to reduce timeout and memory pressure, and higher\nvalues for many small files to improve throughput. Controls file retrieval only — record\npaging is governed by the export's pageSize.","maximum":1000},"sortByFields":{"type":"array","description":"Sorts the records parsed from each file before they are processed, establishing a\ndeterministic processing order (for example, chronological or priority-based). Sorting\nhappens after parsing but before any filtering or grouping, and does not modify the\nsource files. Sorting by the same fields used in groupByFields improves grouping\nperformance.","items":{"type":"object","properties":{"field":{"type":"string","description":"Record field to sort by; use dot notation for nested properties (e.g.\ncustomer.name). Field names are case-sensitive."},"descending":{"type":"boolean","description":"When true, sorts this field in descending order (newest/highest first); when false\nor omitted, sorts ascending. Directions can be mixed across fields in a\nmulti-field sort."}}}},"groupByFields":{"$ref":"#/components/schemas/GroupBy"},"groupEmptyValues":{"type":"boolean","description":"When true, records whose groupByFields values are empty are still grouped together rather\nthan each forming its own group; when false (the default), empty-keyed records are not\ngrouped. Only relevant when groupByFields is set."},"csv":{"type":"object","description":"Parsing settings for delimiter-separated text files (CSV, TSV, pipe-delimited, and\nsimilar). Configure when type is \"csv\".\n\nRequired when type is csv.","properties":{"columnDelimiter":{"type":"string","description":"Character sequence separating fields within each row; comma when omitted. Use \"\\t\"\nfor tab-delimited files; European-locale exports often use semicolons. A wrong\ndelimiter doesn't fail the parse: with a header row, each row becomes one field named\nafter the whole header line (`{\"order_id;sku;qty\": \"1001;TEE-M;2\"}`), so the failure\nshows up in later steps."},"rowDelimiter":{"type":"string","description":"Character sequence marking the end of each record; auto-detected from file content\nwhen omitted. Set explicitly (\"\\n\", \"\\r\\n\", or \"\\r\") only when auto-detection fails,\nsuch as for files with mixed line endings — an incorrect value merges or splits\nrecords."},"hasHeaderRow":{"type":"boolean","description":"When true (the default), the first row is read as field names rather than data, and\nthose names are available in mappings. When false, every row is treated as a data\nrecord and fields are referenced by position."},"trimSpaces":{"type":"boolean","description":"When true, removes leading and trailing whitespace from every field value during\nparsing; when false (the default), whitespace is preserved exactly as in the source.\nHeader row values are always trimmed regardless of this setting, and spaces between\nwords are never affected."},"rowsToSkip":{"type":"integer","description":"Number of rows at the top of the file to ignore before parsing begins — useful for\nreport titles, timestamps, or other metadata above the data. The header row (when\nhasHeaderRow is true) is expected after the skipped rows."},"disableQuoteAndStripEnclosingQuotes":{"type":"boolean","description":"When true, disables CSV quote processing: quotes are treated as literal characters,\nenclosing quotes are stripped, and delimiters inside quoted text split the field.\nWhen false (the default), standard RFC 4180 quoting applies and quoted fields\nprotect embedded delimiters. Enable only for files with non-standard or malformed\nquoting; review this setting first when field counts vary unexpectedly between rows."},"keyColumns":{"type":"array","description":"Field names whose values together identify a record's group when consecutive rows\nshare the same key — used to merge multi-line records (e.g. an order header repeated\nacross line-item rows) into a single record. Leave empty for one record per row; the\ncolumns must exist in the parsed header.","items":{"type":"string"}}}},"json":{"type":"object","description":"Parsing settings for JSON files. Configure when type is \"json\". resourcePath locates the\narray of records when they are nested inside a container object; malformed JSON fails\nthe entire file's processing.\n\nRequired when type is json.","properties":{"resourcePath":{"type":"string","description":"Dot-notation path to the array of records within the JSON structure (e.g.\n\"response.data.customers\"); leave empty when the file's root is already the record\narray. The path must resolve to an array of objects — array indexing and wildcards\nare not supported. A path that resolves to nothing produces zero records without\nraising an error."}}},"xlsx":{"type":"object","description":"Parsing settings for Microsoft Excel (.xlsx) workbooks. Configure when type is \"xlsx\".\nCalculated cell values are extracted rather than formulas; legacy .xls files are not\nsupported — use the modern Open XML format.\n\nRequired when type is xlsx.","properties":{"hasHeaderRow":{"type":"boolean","description":"When true (the default), the first row is read as field names rather than data;\nblank header cells are auto-named and duplicate names are made unique with suffixes.\nWhen false, every row is treated as data and fields receive generic positional names\n(Column1, Column2, ...)."}}},"xml":{"type":"object","description":"Parsing settings for XML documents. Configure when type is \"xml\". resourcePath is an\nXPath expression selecting the elements treated as records; namespaces are handled\nautomatically.\n\nRequired when type is xml.","properties":{"resourcePath":{"type":"string","description":"XPath expression selecting the elements treated as records — each matching element\nbecomes one record, with its child elements as fields. Required for XML parsing;\nthere is no default. Use an absolute path (/Root/Order) when the structure is fixed,\n//Element to match at any depth, or predicates (//Element[@type=\"product\"]) to\nfilter; XPath is case-sensitive, and a non-matching path yields zero records."}}},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"},"fileDefinition":{"type":"object","description":"Parsing via a predefined file definition resource, for formats the standard parsers\ncannot handle — fixed-width files, EDI documents (X12, EDIFACT), and multi-record-type\nor proprietary formats. Configure when type is \"filedefinition\".\n\nRequired when type is filedefinition.","properties":{"_fileDefinitionId":{"type":"string","format":"objectId","description":"File definition resource containing the parsing rules for this format. Must\nreference an existing, accessible file definition — never guess or fabricate the ID.\nDefinitions are reusable across exports, so changing one affects every export that\nuses it."},"allowPartialSuccess":{"type":"boolean","description":"When true, a file whose records partially fail parsing against the file definition\nstill emits the records that did parse, instead of failing the entire file. Applies to\nfile-definition (fixed-width, EDI) parsing only. For X12 and EDIFACT the unit is the\ntransaction set (ST–SE, UNH–UNT), and it takes effect only with an EDI profile linked:\na failed set is set aside and reported as an error while the file's other sets go on\nas records. Without this flag, one failed set fails the whole file."}}},"filter":{"description":"Selects which files are processed: files matching the filter criteria are exported,\nand non-matching files are skipped entirely before processing begins. Filterable\nfields come from each file's listing entry: the name as `fileName`, the size as\n`filesize`, and the modified time as `lastModifiedTime` on FTP, SFTP and S3 or\n`modifiedTime` on cloud-storage folders (`http.type: \"file\"`)\n(e.g. [\"endswith\", [\"string\", [\"extract\", \"fileName\"]], \".csv\"]). There is no\n`lastmodified` key.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"backupPath":{"type":"string","description":"Path where backup copies of source files are stored before processing."},"includeSubdirectories":{"type":"boolean","default":false,"description":"When true, an Azure Data Lake Storage Gen2 transfer (`assistant: azuredatalakestoragegen2`,\n`http.type: file`) also picks up files in the subdirectories below the directory path in\n`http.relativeURI`; when false or omitted only that directory's own files are transferred."},"directory":{"type":"object","required":["pathMode"],"description":"Structured source location for cloud file-provider sources (Google Drive shared drives,\nBox, Dropbox, Google Cloud Storage, Azure Data Lake Storage Gen2), replacing the flat path\nused by classic FTP/S3 sources. Select the location by folder ID or by a path relative to\na configured storage root via pathMode. For the drive-style providers, directoryId mode\naddresses the folder by id alone — the flat relative-path field is not consulted at\nruntime. Google Cloud Storage puts the bucket in `storageRootId` and the object prefix in\n`id` with `pathMode: directoryId`; Azure Data Lake Storage Gen2 puts the container in\n`name` and the directory path in `http.relativeURI`.","properties":{"pathMode":{"type":"string","enum":["relativePath","directoryId"],"description":"How the directory is addressed — by provider folder ID, or by a path relative to a configured storage root."},"id":{"type":"string","description":"Provider-native folder ID of the source directory (e.g. a Google Drive folder ID); set\nwhen pathMode is directoryId. This is the file provider's own ID, not a Celigo resource ID.\nFor Google Cloud Storage it is the object prefix inside the bucket, ending with `/`."},"name":{"type":"string","description":"Display name of the folder identified by id, kept for readability in the UI; the\nfolder browser fills it when the location is picked visually. For Azure Data Lake\nStorage Gen2 it is the authoritative container name instead — the form saves the\ncontainer here and no id."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root (e.g. a Google Drive shared-drive ID) the relative\npath resolves against; set when pathMode is relativePath. The provider's own ID, not a Celigo ID.\nFor Google Cloud Storage it is the bucket name."},"storageRootName":{"type":"string","description":"Display name of the storage root identified by storageRootId, retained for reference in the UI."}}},"backupDirectory":{"type":"object","required":["pathMode"],"description":"Structured destination for backup copies of source files on cloud file providers — the\nfile-provider counterpart to backupPath. Addressed the same way as directory.","properties":{"pathMode":{"type":"string","enum":["relativePath","directoryId"],"description":"How the backup location is addressed — by provider folder ID, or by a path relative to a configured storage root."},"id":{"type":"string","description":"Provider-native folder ID of the backup directory; set when pathMode is directoryId."},"name":{"type":"string","description":"Display name of the backup folder identified by id, kept for readability in the UI;\nthe folder browser fills it when the location is picked visually."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root the backup relative path resolves against; set when pathMode is relativePath."}}}}},"IncludeParentData":{"type":"boolean","description":"When true and a path to records is configured on the export (`pathToRecords` for MongoDB and DynamoDB, `http.response.resourcePath` for HTTP, `file.json`/`file.xml` `resourcePath` for file-based exports), the data outside that path is attached to each extracted record as a `_PARENT` object, keeping the surrounding envelope available to downstream steps. When omitted or false, records are emitted without `_PARENT`, exactly as before. Constraints: on HTTP exports this option cannot be combined with `useXpathParser: true` (rejected at save with `EXPORT_INCLUDE_PARENT_DATA_XPATH_UNSUPPORTED`), and XML responses must resolve to the default converted-JSON parse mode — a run whose XML parser resolves to DOM/XPath mode (for example, because a connection-level field uses an advanced XPath expression) fails at start with the same error rather than silently omitting `_PARENT`. On file-based exports, it requires `file.type` of `json` with `file.json.resourcePath` configured — XML file envelope capture is not yet supported (the streaming XML parser emits no envelope event; XML support arrives with a parsers-package enhancement) — and any other file configuration is rejected at save with `EXPORT_INCLUDE_PARENT_DATA_UNSUPPORTED_FILE_TYPE`."},"Filter":{"type":"object","description":"Configuration for selectively processing records based on specified criteria. This object enables\nprecise control over which items are included or excluded from processing operations.\n\n**Filter behavior**\n\nWhen configured, the filter is applied before processing begins:\n- Items that match the filter criteria are processed\n- Items that don't match are completely skipped\n- No partial processing is performed\n\nSend the whole object. A rules array sent alone as the `filter` value is not\nstored, and a full update of an export that carries one removes the filter the\nexport had, with no error.\n\n**Implementation approaches**\n\nThere are two distinct filtering mechanisms available:\n\n**Rule-Based Filtering (`type: \"expression\"`)**\n- **Best For**: Common filtering patterns based on standard attributes\n- **Capabilities**: Filter by names, values, dates, numerical ranges, text patterns\n- **Advantages**: Declarative, no coding required, consistent performance\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear, static criteria for selection\n\n**Script-Based Filtering (`type: \"script\"`)**\n- **Best For**: Complex logic, dynamic criteria, or business rules\n- **Capabilities**: Full programmatic control, access to complete metadata\n- **Advantages**: Maximum flexibility, can implement any filtering logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Simple rules aren't sufficient or logic needs to be dynamic\n","properties":{"type":{"type":"string","description":"Determines which filtering mechanism to use. This choice affects which properties\nmust be configured and how filtering logic is implemented.\n\n**Available types**\n\n**Rule-Based Filtering (`\"expression\"`)**\n- **Required Config**: The `expression` object with rule definitions\n- **Behavior**: Evaluates declarative rules against item attributes\n- **Best For**: Common patterns like name matching, date ranges, value limits\n- **Advantages**: Simpler to configure, no custom code required\n\n**Script-Based Filtering (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to determine which items to process\n- **Best For**: Complex conditions, business logic, dynamic criteria\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard filtering needs (name, size, date), use `\"expression\"`\n2. For complex logic or conditions not covered by expressions, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based filtering. This object enables filtering\nitems based on common attributes without requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define filtering rules that\ncan match against item attributes like name, type, value, date, and other properties.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Rules can be combined with AND/OR logic\n- Each rule can check a specific attribute\n- Multiple conditions can be applied (ranges, pattern matching, exact matches)\n\n**Common filter patterns**\n\n1. **Pattern matching**: Regular expressions with `matches`\n2. **Value range filtering**: Numbers between min and max values\n3. **Date range filtering**: Items created/modified within specific time ranges\n4. **Status checking**: Items with specific status values or properties\n\nFor AI agents: Rule-based filtering should be your first choice when the filtering criteria\ncan be expressed in terms of standard attributes. Only use script-based filtering when\nmore complex logic is required.\n","properties":{"version":{"type":"string","description":"Version identifier for the expression format. Currently only version \"1\" is supported.\n\nThis field ensures future compatibility if the expression format evolves. Always set to \"1\"\nfor current implementations.\n","enum":["1"]},"rules":{"type":"array","description":"Expression array defining filter conditions using prefix notation. The first element is the operator,\nfollowed by its operands which may themselves be nested expression arrays.\n\nThe rule expression follows this pattern:\n- First element: Operator name (string)\n- Remaining elements: Operands for that operator (values or nested expressions)\n\n**Expression structure**\n\nFilter expressions use a prefix notation where operators appear before their operands:\n```\n[operator, operand1, operand2, ...]\n```\n\n**Comparison Operators**\n- `\"equals\"`: Exact match (equals)\n- `\"notequals\"`: Not equal to value (not equals)\n- `\"greaterthan\"`: Value is greater than specified value (is greater than)\n- `\"greaterthanequals\"`: Value is greater than or equal to specified value (is greater than or equals)\n- `\"lessthan\"`: Value is less than specified value (is less than)\n- `\"lessthanequals\"`: Value is less than or equal to specified value (is less than or equals)\n- `\"startswith\"`: String starts with specified prefix (starts with)\n- `\"notstartswith\"`: String does not start with specified prefix (does not start with)\n- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"notendswith\"`: String does not end with specified suffix (does not end with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"empty\"`: Field is missing or empty (is empty)\n- `\"notempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches a regular expression, ignoring case (matches)\n\nOperator names are case-insensitive: `NOTEMPTY` and `endsWith` work and are stored\nas sent. `isempty`, `isnotempty`, `anyof`, `notcontains`, `doesnotstartwith`,\n`doesnotendwith`, `notmatches`, `doesnotmatch` and `notequal` are not operators:\nthey are refused with 422 \"Invalid operation: <name>\", with the name in lowercase,\nboth when the filter is saved and by `POST /v1/processors/filter`. To negate a\ncondition, wrap it in `not`.\n\nThe string comparisons (`equals`, `contains`, `startswith`, `endswith` and their\nnegations) are case-sensitive; wrap the field in `lowercase` to compare without case.\n`matches` is not: every pattern is compiled with the flags `gi`, so `^paid$` also\nmatches `PAID` and `[a-z]` matches capital letters. An inline flag such as `(?i)`\nmakes the pattern invalid. The filter still saves, but in a run every record fails\nwith `FILTER_EVALUATION_FAILED` (\"Invalid regular expression: /(?i)…/gi: Invalid\ngroup\"), and `POST /v1/processors/filter` refuses the call with 422 (live-verified\n2026-10-22).\n\n**Logical Operators**\n- `\"and\"`: All conditions must be true\n- `\"or\"`: At least one condition must be true\n- `\"not\"`: Negates the condition\n\n**Field Access and Type Conversion**\n- `\"extract\"`: Access a field from the item by name\n- `\"settings\"`: Access a custom setting from the flow, flow step, or integration configuration\n- `\"boolean\"`: Convert value to Boolean type\n- `\"epochtime\"`: Convert value to Epoch Time (Unix timestamp)\n- `\"number\"`: Convert value to Number type\n- `\"string\"`: Convert value to String type\n\n**Field Access Details**\n\n**Using `extract` to access record fields:**\n- Retrieves values from the current record being processed\n- Can access nested properties using dot notation (e.g., `\"customer.email\"`)\n- Paths are relative to the record itself: `[\"extract\", \"record.status\"]`\n  matches nothing\n- In a record filter, must be wrapped in a type conversion (`string`, `number`,\n  `boolean` or `epochtime`): a bare `[\"extract\", ...]` operand is refused with 422\n  \"Field operation extract must be called by a Type Conversion Operation but was called\n  by <operator>\", both when an import's filter is saved and by\n  `POST /v1/processors/filter`\n\n**Using `settings` to access configuration values:**\n- Retrieves values from the integration's configuration settings\n- Supports different scopes with prefix notation:\n  - `flow.settingName`: Access flow-level settings\n  - `flowGrouping.settingName`: Access the flow group's settings\n  - `export.settingName`: Access the export's (or lookup's) own settings\n  - `import.settingName`: Access the import's own settings\n  - `integration.settingName`: Access integration-level settings\n  - `connection.settingName`, `iClient.settingName`: Access the step's\n    connection and iClient settings\n- A scope whose resource has no settings can be absent; a rule that reads\n  it matches nothing\n- Must be wrapped in a type conversion like `extract`: a bare\n  `[\"settings\", ...]` operand is refused with 422 \"Field operation settings\n  must be called by a Type Conversion Operation\"\n- Useful for dynamic filtering based on configuration\n\n**Field Transformations**\n- `\"lowercase\"`: Convert string to lowercase\n- `\"uppercase\"`: Convert string to uppercase\n- `\"ceiling\"`: Round number up to the nearest integer\n- `\"floor\"`: Round number down to the nearest integer\n- `\"abs\"`: Get absolute value of a number\n\n**Arithmetic** (number operands)\n- `\"add\"`, `\"subtract\"`, `\"multiply\"`, `\"divide\"`, `\"modulo\"`: for example\n  `[\"subtract\", [\"number\", [\"extract\", \"total\"]], [\"number\", [\"extract\", \"discount\"]]]`.\n  `mod` is not an operator (422 \"Invalid operation: mod.\").\n\n**Dates**\n- `epochtime` reads ISO 8601 strings and epoch-millisecond numbers. An empty\n  string, a string that isn't a date, or epoch milliseconds sent as a string\n  fail it with \"Invalid date. Cannot convert given string or number to date\n  time.\", followed by `; record=` and the record as JSON: in a run that record fails (`FILTER_EVALUATION_FAILED`, source\n  `output_filter` on an export), and `POST /v1/processors/filter` and an\n  export preview refuse the whole call with 422. `and` and `or` evaluate\n  every operand, so a `notempty` test beside it doesn't prevent the failure.\n  A missing or null field doesn't match.\n- `datetime`, `date` and `timestamp` are not type conversions: 422\n  \"Invalid operation: <name>.\"\n- `number` reads a missing or empty field as 0.\n\n**Handlebars operands**\n- An operand can also be a string holding exactly one Handlebars expression, such as\n  `\"{{record.status}}\"` or `\"{{lowercase record.code}}\"`, on either side of the\n  operator. It renders against the record (`record.<field>`; an unprefixed\n  `{{status}}` renders empty) before the comparison.\n- The rendered value is a string: wrap it in `[\"number\", ...]` to compare it with a\n  number, or the rule is refused with 422.\n- `epochtime` does not render Handlebars: `[\"epochtime\", \"{{timestamp}}\"]` is refused\n  with 422 \"invalid datetime format\".\n- A string with text around the expression, or with more than one expression, is\n  compared as a literal.\n- `[\"number\", \"{{timestamp \\\"x\\\"}}\"]` is the current time in epoch milliseconds,\n  so `[\"greaterthanequals\", [\"epochtime\", [\"extract\", \"created\"]], [\"subtract\",\n  [\"number\", \"{{timestamp \\\"x\\\"}}\"], 604800000]]` keeps records created in the\n  last 7 days.\n- An unknown helper fails the rule with 422 \"Failed to evaluate handlebars\n  expression\".\n\nChoose the type conversion that matches the comparison — `number` for numeric\ncomparisons, `epochtime` for dates, `string` for text. For example:\n\n```json\n[\n  \"equals\",\n  [\n    \"number\",  // Convert to number before comparison\n    [\n      \"extract\",\n      \"quantity\"\n    ]\n  ],\n  100\n]\n```\n\nExample with datetime conversion:\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",  // Convert to Unix timestamp before comparison\n    [\n      \"extract\",\n      \"createdDate\"\n    ]\n  ],\n  1609459200000  // January 1, 2021 as Unix timestamp in milliseconds\n]\n```\n\nExample with transformations:\n```json\n[\n  \"and\",\n  [\n    \"matches\",\n    [\n      \"lowercase\",  // Convert to lowercase before matching\n      [\n        \"string\",\n        [\n          \"extract\",\n          \"categories\"\n        ]\n      ]\n    ],\n    \"netsuite\"\n  ],\n  [\n    \"notequals\",\n    [\n      \"string\",\n      [\n        \"extract\",\n        \"recurrence.pattern.type\"\n      ]\n    ],\n    \"\"\n  ]\n]\n```\n\nExample comparing a record field with a flow setting:\n```json\n[\n  \"equals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"trantype\"\n    ]\n  ],\n  [\n    \"string\",\n    [\n      \"settings\",\n      \"flow.trantype\"\n    ]\n  ]\n]\n```\n\n**Examples**\n\nExample 1: Status field is not equal to \"cancelled\"\n```json\n[\n  \"notequals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"status\"\n    ]\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"filename\"\n    ]\n  ],\n  \"HC\"\n]\n```\n\nExample 3: Amount is greater than 100\n```json\n[\n  \"greaterthan\",\n  [\n    \"number\",\n    [\n      \"extract\",\n      \"amount\"\n    ]\n  ],\n  100\n]\n```\n\nExample 4: Order date is after January 1, 2023 (UTC). Compare dates as `epochtime`\nagainst epoch milliseconds: a `string` comparison of ISO timestamps orders values with\ndifferent UTC offsets wrongly.\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",\n    [\n      \"extract\",\n      \"orderDate\"\n    ]\n  ],\n  1672531200000\n]\n```\n","items":{"oneOf":[{"title":"String","type":"string"},{"title":"Number","type":"number"},{"title":"Boolean","type":"boolean"},{"title":"Object","type":"object"},{"title":"Array","type":"array"},{"title":"Null","type":"null"}]}}}},"script":{"type":"object","description":"Configuration for programmable script-based filtering. This object enables complex, custom\nfiltering logic beyond what expression-based filtering can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to determine which items\nshould be processed.\n\n**Implementation approach**\n\nScript-based filtering works by:\n1. Executing the specified function from the referenced script\n2. Passing item data to the function\n3. Using the function's return value (true/false) to determine inclusion\n\n**Common use cases**\n\nScript filtering is ideal for:\n- Complex business logic that can't be expressed as simple rules\n- Dynamic filtering criteria that change based on external factors\n- Content-based filtering that requires deep inspection\n- Advanced pattern matching beyond simple wildcards\n- Multi-stage filtering with intermediate logic\n\nFor AI agents: Only use script-based filtering when expression-based filtering is insufficient.\nScript filtering requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to the Script resource that contains the filtering logic. This must be a valid\nObjectId of a Script resource in this account: any other id fails the save with 422\n(\"Only a script owned by the user or a script shared by the Integration App can be used\nat filter.script._scriptId.\").\n\nThe referenced script must contain the function specified in the `function` field\nand must be written to handle filtering specifically. The script receives\nitem data as its input and must return a boolean value indicating whether\nto process the item (true) or skip it (false).\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n"},"function":{"type":"string","description":"Name of the function within the script to execute for filtering decisions. This function\nmust exist in the script referenced by _scriptId.\n\n**Function requirements**\n\nThe specified function must:\n- Accept item data as its first parameter\n- Return a boolean value (true to process the item, false to skip it)\n- Handle errors gracefully\n- Execute efficiently (as it may run for many items)\n\n**Function signature**\n\n```javascript\nfunction filterItems(itemData) {\n  // itemData contains properties of the item being evaluated\n  // Custom logic here\n  return true; // or false to skip the item\n}\n```\n\nFor AI agents: Ensure the function name exactly matches a function defined in the\nreferenced script, as mismatches will cause the filter to fail.\n"}}}}},"Distributed":{"type":"object","description":"Authentication settings for distributed (real-time listener) exports.","properties":{"bearerToken":{"type":"string","description":"Bearer token that a system pushing data into this export sends as\n`Authorization: Bearer <token>`. Optional — most distributed listeners (NetSuite,\nSalesforce real-time) use no token. Write-only and stored encrypted; treat it as a\nsensitive credential.","format":"password"}}},"Cdc":{"type":"object","description":"Change-data-capture configuration for real-time listener exports (the \"Listen for real-time\ndata\" source step). Present only when `type` is `stream`. Set on `MongodbExport` (MongoDB change\nstreams) and `RDBMSExport` (SQL Server CDC and PostgreSQL logical replication); the listener\nstreams change events continuously instead of fetching records on a schedule.\n\nOn save the API normalizes the `properties` grid into the typed fields: a `snapshot.mode`\nentry populates `snapshotMode` and a `capture.mode` entry populates `captureMode`. Every\nstream export must resolve a snapshot mode from one of those two places.","properties":{"captureMode":{"type":"string","enum":["change_streams_update_full","change_streams_update_full_with_pre_image"],"default":"change_streams_update_full","description":"How MongoDB change-stream events are materialized into records. Use\n`change_streams_update_full_with_pre_image` when downstream steps need the document's prior\nstate. MongoDB listeners only — `RDBMSExport` (SQL Server CDC, PostgreSQL) does not set this."},"snapshotMode":{"type":"string","enum":["initial","no_data","when_needed","initial_only"],"default":"no_data","description":"Whether the listener seeds existing rows before streaming changes. Use `initial` to backfill\nexisting data on the first run; `no_data` to stream only new changes."},"slotName":{"type":"string","pattern":"^[a-z_][a-z0-9_]{0,62}$","description":"Name of the PostgreSQL logical replication slot the listener consumes. PostgreSQL listeners\nonly. When omitted, the platform manages the slot name. Must be a valid PostgreSQL\nidentifier (lowercase letters, numbers, or underscores, starting with a letter or\nunderscore)."},"publicationName":{"type":"string","pattern":"^[a-z_][a-z0-9_]{0,62}$","description":"Name of the PostgreSQL publication that defines which tables stream changes. PostgreSQL\nlisteners only. When set, the publication is the sole table filter and `rdbms.tables`\nstays unset. Must be a valid PostgreSQL identifier."},"cursor":{"type":"string","description":"Server-managed stream position marker (where the listener resumes reading change events).\nMaintained by the platform as events are consumed; the UI exposes a reset workflow for it\nunder Cursor Management."},"paths":{"type":"array","items":{"type":"string"},"description":"Dot-notation paths within each raw change event to retain in the emitted record. MongoDB\nlisteners default to `[\"payload\"]` (the whole change document); SQL Server and PostgreSQL\nlisteners commonly select `payload.after`, `payload.op`, and related fields to expose the\nchanged row and operation type."},"intervalTime":{"type":"integer","default":300,"description":"Polling interval, in seconds, at which the listener checks the source for new change events."},"properties":{"type":"array","description":"Connector-engine settings passed through to the underlying CDC connector as name/value\npairs. Mirrors the form's additional-properties grid: the required keys (`capture.mode`\nand `snapshot.mode` for MongoDB, `snapshot.mode` for SQL Server and PostgreSQL) are\nserialized first, followed by optional keys such as `field.exclude.list`,\n`column.include.list`, or `snapshot.collection.filter.overrides.<db>.<collection>`.\nOn save the API also lifts `snapshot.mode` / `capture.mode` entries into the typed\n`snapshotMode` / `captureMode` fields.","items":{"type":"object","required":["name","value"],"properties":{"name":{"type":"string","description":"Connector property key (e.g. `capture.mode`, `snapshot.mode`, `field.exclude.list`)."},"value":{"type":"string","description":"Connector property value."}}}}}},"Topic":{"type":"object","description":"Subscription settings for an Event Streams listener (`adaptorType: TopicExport`). The\nlistener consumes messages published to the topic and hands them to the flow in batches;\nthe platform delivers them internally, so the export carries no `_connectionId`. Present\nwhen `adaptorType` is `TopicExport`; the server fills `startPosition` and `maxWaitTimeMs`\nwith their defaults when they are omitted.","properties":{"_topicId":{"type":"string","format":"objectId","description":"Topic this listener subscribes to. Create or look it up with `GET /v1/topics`; the\ntopic must belong to the same account. Pointing an existing listener at a different\ntopic starts a new subscription, read from `startPosition`. A clone made in the same\nenvironment — of this export, or of a flow that contains it — keeps `_topicId`, so the\ncopy becomes another listener on the topic once its flow is enabled."},"startPosition":{"type":"string","enum":["latest","earliest"],"default":"latest","description":"Where the listener starts reading when it first subscribes to `_topicId`. Use `earliest`\nto backfill retained history, `latest` (the default) to process only new messages. Read\non create and whenever `_topicId` changes; on other updates the stored value is kept even\nif a different value is sent."},"maxWaitTimeMs":{"type":"integer","minimum":1000,"default":30000,"description":"Longest time, in milliseconds, the listener waits to fill a batch before delivering\nwhatever it has collected. Lower values reduce latency for sparse topics; the default\nof 30 seconds favors fuller batches. The batch record cap is the export's `pageSize`."}}},"FileSystem":{"type":"object","description":"Defines which files to read from a local or mounted folder on the host running the\non-premise agent. Required when the _connectionId field references a file-system (on-premise)\nconnection; must not be included for other connection types.","required":["directoryPath"],"properties":{"directoryPath":{"type":"string","description":"Folder on the on-premise agent's host to read files from; the agent's OS account must\nhave read permission on it. Accepts a local OS path or a UNC network share, and supports\nhandlebars templates for dynamic folders."}}},"Http":{"type":"object","description":"Configuration for HTTP exports. Required when the export's `_connectionId` references a\nconnection of type `http`, and on `NetSuiteHTTPExport` (NetSuite's REST APIs on a `netsuite`\nconnection, where `nsDomainType` supplies the host).","properties":{"type":{"type":"string","enum":["file","blob","csvstream"],"description":"Set to `file` when the export works with files rather than a record-returning endpoint: raw file downloads (PDFs, images, binary data) and the folder mode of the cloud file providers (Google Drive, Box, Dropbox, Azure Blob, Google Cloud Storage, Celigo Storage), where each file in the folder is parsed by the `file` object's `type` (csv, json, xml, xlsx, filedefinition — including EDI) into records; the `file` object must also be configured. Leave undefined for standard data exports that return structured records (JSON, XML, GraphQL, SOAP responses) — the response is then parsed into records for downstream steps. When set to `file` or `blob`, the export appears as a \"Transfer\" step in Flow Builder instead of a standard \"Export\" step."},"formType":{"type":"string","enum":["http","rest","graph_ql","assistant","assistant_graphql"],"description":"Authoring mode of the HTTP request form. Controls which editor the UI presents\n(standard HTTP, legacy REST, GraphQL, or connector-assistant driven) and how the\nrequest fields are interpreted. When omitted, the standard HTTP form is used."},"method":{"type":"string","description":"HTTP method used to request data from the target API. Consult the target API's\ndocumentation to determine the appropriate method. `HEAD` exists for connector endpoints\nthat return metadata in response headers only (Azure Data Lake Storage Gen2 path\nproperties).","enum":["GET","POST","PUT","PATCH","DELETE","HEAD"]},"followRedirects":{"type":"boolean","default":true,"description":"When explicitly false, 3xx responses are not followed — the redirect\nresponse itself (status code, `Location` header, body) becomes the\nrecord. Omitted or true follows allowed redirects (the default\nbehavior)."},"maxRedirects":{"type":"integer","minimum":1,"maximum":10,"description":"Caps how many consecutive 3xx redirects are followed. Only applies\nwhen `followRedirects` is not false; to not follow at all, set\n`followRedirects: false` rather than `maxRedirects: 0`. Decimal\nvalues are rejected on save."},"relativeURI":{"type":"string","description":"Resource path appended to the connection's `baseURI` to form the complete request URL. Path segments, query parameters, or the entire value can be built with handlebars expressions for endpoints determined at runtime. For lookup exports (`isLookup: true`) with mappings configured, handlebars always render against the original pre-mapped input record, so fields removed or renamed by mappings remain available for URI construction. On `NetSuiteHTTPExport` the base URL is derived from the NetSuite connection and `nsDomainType`, so the path starts at `/services/rest/...` or `/app/site/hosting/restlet.nl`.\n\nLeave it empty to call the connection's `baseURI` exactly; `/` appends a trailing slash, which is a different URL. An export saved with an empty relative URI and no `http.body` comes back from reads without its `http` block, so a PUT built from such a read loses the stored settings: send the complete `http` block with every PUT."},"headers":{"type":"array","description":"Headers specific to this export, merged with (and able to override) headers defined on the connection. Define common headers such as authentication on the connection instead. Values support handlebars expressions; for lookup exports with mappings, values render against the pre-mapped input record.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header to send; a header defined here overrides a same-named header from the connection."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions; for lookup exports with mappings, the value renders against the pre-mapped input record."}}}},"requestMediaType":{"type":"string","description":"Overrides the connection-level request media type for this export. Set only when this endpoint requires a different format than the connection default.","enum":["json","xml","urlencoded","form-data","plaintext"]},"body":{"type":"string","description":"Request body sent with POST, PUT, or PATCH requests, typically carrying query or filter criteria for APIs (such as GraphQL or SOAP) that expect them in the body. The content must match the format set by `requestMediaType` and supports handlebars expressions for dynamic values."},"successMediaType":{"type":"string","description":"Media type used to parse successful response bodies. Set only when the response format differs from the request format.","enum":["json","xml","csv","plaintext"]},"errorMediaType":{"type":"string","description":"Media type used to parse error response bodies. Set only when error responses use a different format than the request.","enum":["json","xml","plaintext"]},"_asyncHelperId":{"type":"string","format":"objectId","description":"AsyncHelper resource that handles polling for long-running operations on APIs that process requests asynchronously (HTTP 202 responses, job tickets, feed or document IDs). Set when the export must submit a request, poll for status, and retrieve results once the external process completes — for example Amazon SP-API feeds or large report generators."},"once":{"type":"object","description":"Callback configuration for once exports, used to mark records as exported in the source system after successful processing.\nCalled only while the export's `type` is `once`: an export with no `type` keeps a saved `once` and never calls it, so every\nrun reads every record again (live-verified 2026-10-22).","properties":{"relativeURI":{"type":"string","description":"Relative path (starting with `/`) called on the source system to mark each record as exported after successful processing. Supports handlebars variables and renders against the pre-mapped record — mappings do not apply to the callback URI."},"method":{"type":"string","description":"HTTP method used for the mark-as-exported callback request.","enum":["GET","PUT","POST","PATCH","DELETE"]},"body":{"type":"string","description":"Request body sent with the mark-as-exported callback. Supports handlebars expressions for dynamic values."}}},"paging":{"type":"object","description":"Controls how the export requests subsequent pages when the API returns multi-page responses. The `method` field determines which companion fields are required and how each next page is requested. For the page, skip, and token methods, reference the matching pagination variable (`{{export.http.paging.page}}`, `{{export.http.paging.skip}}`, or `{{export.http.paging.token}}`) in the relative URI or request body — misconfigured pagination is a common cause of incomplete data retrieval.\n\nWhen this export is the first lookup of a builder-mode API with `pagination.enabled` set, it is that API's volume driver: saving the export (even unchanged) invalidates the API's issued cursors, so a caller mid-walk gets an empty final page, and removing `paging` is accepted on save but makes every call to the API fail with `400 api_pagination_source_not_paged` until paging is restored or the API's flag is turned off.\n\nWhen a next page's response is identical to the previous one, paging stops: the export records an open error with code `page_response_error` (\"Identical response received from next page request. No more page requests will be made.\") and the run completes with the pages already read, so later pages are never exported. A request that doesn't change between pages produces this, as observed with `method: page` and the pagination variable missing from the relative URI and body; reference the variable so each request asks for the next page.","properties":{"method":{"type":"string","description":"Pagination strategy used to request each subsequent page; match it to the mechanism documented by the target API. Determines which companion fields are required: `token` needs `path` and `pathLocation`, `url` needs `path`, `relativeuri` needs `relativeURI`, `body` needs `body`, while `linkheader` typically needs no extra configuration. Using the wrong method results in errors or incomplete data retrieval.","enum":["linkheader","page","skip","token","url","relativeuri","body"]},"page":{"type":"integer","description":"Starting page number for `method: page`. Set to 1 for APIs whose first page is not zero-indexed; when omitted, paging starts at 0. The value is incremented automatically for each subsequent page request."},"skip":{"type":"integer","description":"Starting offset for `method: skip`. Rarely needed — most APIs start at 0, and the value is incremented by the page size automatically for each subsequent request. Set only when the API requires a non-zero starting offset."},"token":{"type":"string","description":"Initial token for `method: token`. Leave empty for normal pagination — the first request is sent without a token, and subsequent tokens are extracted from each response via `path`. Set only to resume from a known token or for APIs that require a token on the first request."},"path":{"type":"string","description":"Location of the pagination value in each response — the continuation token for `method: token`, or the complete next-page URL for `method: url`. When `pathLocation` is `body`, use a dot-notation JSON path (e.g. `meta.nextToken`); when `header`, use the exact case-sensitive header name. Not used by other pagination methods."},"pathLocation":{"type":"string","description":"Where the export looks for the pagination token referenced by `path`. Required for `method: token`; not used by other pagination methods.","enum":["body","header"]},"pathAfterFirstRequest":{"type":"string","description":"Alternative token location used for responses after the first page, in the same format as `path`. Set only when the API moves the token to a different location in subsequent responses — setting it unnecessarily can cause pagination to fail."},"relativeURI":{"type":"string","description":"Overrides the main relative URI for second and later page requests; leave empty when the main relative URI works for all pages. Build it with handlebars using the `previous_page` context — `previous_page.full_response` (the prior response body), `previous_page.last_record` (the last record of the prior page), and `previous_page.headers` (the prior response headers)."},"body":{"type":"string","description":"Overrides the main request body for second and later page requests, typically for GraphQL or SOAP APIs that paginate through the body; leave empty when the main body works for all pages. Build it with handlebars using the `previous_page` context — `previous_page.full_response`, `previous_page.last_record`, and `previous_page.headers`."},"mergeBodyParamsToPagingBody":{"type":"boolean","description":"Only applies when `paging.method` is `body`. When true, the body\nparameters produced by the export's mappings at runtime are merged\ninto the evaluated `paging.body` for page 2+ requests, so paging\nrequests keep the same mapped parameters as the first page.\nOmitted/false keeps the original behavior — page 2+ requests send\nonly the evaluated `paging.body`."},"linkHeaderRelation":{"type":"string","description":"Link header relation followed for `method: linkheader` when the API uses a value other than the default `next`. Case-sensitive and must exactly match the `rel` value in the Link header, without the `rel=` prefix."},"resourcePath":{"type":"string","description":"Overrides the path to records for second and later page responses. Set only when follow-up pages place records at a different location than the first response; leave empty when all pages share the same structure."},"lastPageStatusCode":{"type":"integer","description":"HTTP status code that signals the last page, replacing the default behavior of treating 404 as the end of pagination. When this status is received (any code but `200` paired with `lastPagePath`), paging stops and the response is not treated as an error. Set it when the API uses a non-404 code (such as 204 or 400) to indicate no more pages, or to `200` together with `lastPagePath` when the last page is an ordinary 200 response: then the response is parsed, its records are kept, and paging stops only when `lastPagePath` matches `lastPageValues`."},"lastPagePath":{"type":"string","description":"JSON path to a response-body field that signals the end of pagination, such as a \"has more\" flag or a cursor that empties on the last page. Must be used with `lastPageValues`, which lists the values at this path that stop paging. If the path does not exist in a response, the condition is not considered met. With `method: token` the path is read only on a response whose status code equals `lastPageStatusCode`, so pair it with `lastPageStatusCode: 200` for an ordinary last page; with `method: body` it is read on every response."},"lastPageValues":{"type":"array","description":"Values at `lastPagePath` that stop pagination; a match on any entry ends paging. All entries are compared as exact, case-sensitive strings, even for boolean or numeric fields — use `\"true\"`, `\"false\"`, `\"null\"` for JSON null, or `\"\"` for an empty string. Required when `lastPagePath` is set.","items":{"type":"string"}},"maxPagePath":{"type":"string","description":"JSON path to the total page count in the response, used to stop paging once the last page is reached. Only applies to the `page` and `skip` methods. Point it at the total number of pages, not the current page number."},"maxCountPath":{"type":"string","description":"JSON path to the total record count in the response, used to stop paging once all records have been retrieved. Only applies to the `page` and `skip` methods; when both are set, `maxPagePath` takes precedence. Point it at the total number of records, not the count in the current page."}},"if":{"properties":{"method":{"const":"body"}},"required":["method"]},"then":{"required":["body"]},"else":{"if":{"properties":{"method":{"const":"token"}},"required":["method"]},"then":{"required":["path","pathLocation"]},"else":{"if":{"properties":{"method":{"const":"relativeuri"}},"required":["method"]},"then":{"required":["relativeURI"]},"else":{"if":{"properties":{"method":{"const":"url"}},"required":["method"]},"then":{"required":["path"]}}}}},"response":{"type":"object","description":"Controls how records are extracted from the API response and how success or failure is detected at the response level. When the API wraps records in an envelope object, set `resourcePath` to the path of the records array — without it, the entire response body is treated as a single record. Leave this object undefined when the API returns a bare JSON array.","properties":{"resourcePath":{"type":"string","description":"Dot-separated path to the array of records inside the response body (e.g. `data.customers` for `{\"data\": {\"customers\": [...]}}`); without it, a wrapped response is treated as a single record. Values starting with `$` are evaluated as JSONPath instead of dot notation — including unions (`$['orders','invoices'][*]`) and filter expressions (`$.items[?(@.qty>3)]`); all other values keep the existing dot-notation behavior unchanged. Leave undefined when the API returns a bare JSON array. This extracts records from the API response — not to be confused with `oneToMany`/`pathToMany`, which unwrap arrays from input records, or `paging.resourcePath`, which applies only to subsequent page responses."},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"},"resourceIdPath":{"type":"string","description":"Path to the unique identifier within each record, used primarily when processing results of asynchronous import responses. When omitted, the system looks for standard `id` or `_id` fields automatically."},"successPath":{"type":"string","description":"Path to a response field that signals whether the call succeeded, for APIs that return HTTP 200 even on failure. With `successValues`, the value at this path must match one of them; without `successValues`, the path only has to resolve — a response in which it is absent fails with `response_failure` (\"Unable to extract paths from response body using path: <path>, Response: …\")."},"successValues":{"type":"array","items":{"type":"string"},"description":"Values at `successPath` that mark the response as successful; any other value is treated as an error. All comparisons are string-based — use `\"true\"` or `\"false\"` for boolean fields."},"errorPath":{"type":"string","description":"Path to the error message in the response body. The value at this path is included in error logs and error records when the API returns an error; without it, the message is the whole response body."},"failPath":{"type":"string","description":"Path to a response field that signals failure even when the HTTP status code is 200 — the inverse of `successPath`. With `failValues`, the response fails when the value there matches one of them; without, when the path holds a truthy value. A path that resolves to an array matches only when the array has exactly one element."},"failValues":{"type":"array","items":{"type":"string"},"description":"Values at `failPath` that mark the response as failed, even when the HTTP status code is 200."},"allowArrayforSuccessPath":{"type":"boolean","description":"When true, treats the value at `successPath` as an array and counts the response as successful if any element matches `successValues`, rather than requiring a single scalar match. Set it for APIs that return per-record status arrays in a batch response."},"twoDArray":{"type":"object","description":"Parsing options for endpoints that return tabular data as a two-dimensional array (rows of cells) rather than an array of objects, such as spreadsheet-style or report APIs.","properties":{"hasHeader":{"type":"boolean","description":"When true, the first row is treated as column headers and used to name the fields of each generated record."},"doNotNormalize":{"type":"boolean","description":"When true, the rows are passed through as raw arrays instead of being normalized into keyed records."}}},"blobFormat":{"type":"string","description":"Controls how the binary response body is decoded for blob exports. Only relevant when `http.type` is `file` or the export type is `blob`.","enum":["utf8","ucs2","utf-16le","ascii","binary","base64","hex"]}}},"_httpConnectorVersionId":{"type":"string","format":"objectId","description":"Pins the export to a version of the published HTTP connector its connection is bound to\n(`connection.http._httpConnectorId`), so the UI renders that connector's endpoint form instead\nof a hand-authored request. Linked on save together with `_httpConnectorResourceId` and\n`_httpConnectorEndpointId` — see that field for the rule."},"_httpConnectorResourceId":{"type":"string","format":"objectId","description":"The connector resource (a group of related endpoints, such as Orders) that this export's\nendpoint belongs to. Linked on save together with `_httpConnectorEndpointId` — see that field\nfor the rule."},"sendAuthForFileDownloads":{"type":"boolean","description":"When true, includes authentication headers when downloading files."},"_httpConnectorEndpointId":{"type":"string","format":"objectId","description":"The connector endpoint this export calls, shown in the UI as the selected endpoint. On save the\nplatform derives it (with the version and resource ids) from `relativeURI` and `method` when\nthey match an endpoint of the connector the connection is bound to — path parameters, literal\nids and query strings still match — and replaces ids that belong to another connector. An id for\nthe connection's own connector is kept as sent even when the URI names a different endpoint;\nwhen nothing matches, or the connection has no connector, whatever was sent is kept and nothing\nis derived."}}},"Salesforce":{"type":"object","description":"Configuration for Salesforce exports. Required when the export's `_connectionId` references a Salesforce connection; omit for other connection types. The `type` field selects the extraction mode: `soql` runs batch queries and requires the `soql` object, while `distributed` listens for real-time events and requires the `distributed` object. File retrieval (when the export's `type` is `blob`) requires `sObjectType` and `id` instead.","properties":{"type":{"type":"string","description":"Selects the extraction mode and determines which configuration object is required. Use `soql` for scheduled or on-demand batch queries and lookups — it requires the `soql` object and works with both the `rest` and `bulk` APIs and the standard, delta, test, and once export types. Use `distributed` for real-time event handling — it requires the `distributed` object, always appears as a Listener in Flow Builder, ignores the `api` field, and supports only the standard export type.","enum":["soql","distributed"]},"sObjectType":{"type":"string","description":"API name of the Salesforce object the export operates on; the object must exist in the connected org and be accessible to the integration user. Required for distributed exports and for blob exports — blob exports accept only the file storage objects (`Attachment`, `ContentVersion`, `Document`) paired with the `id` field. Optional for SOQL exports, where the object can be inferred from the query."},"id":{"type":"string","description":"Salesforce record ID of the file to retrieve; required for blob exports and not used for `soql` or `distributed` exports. Accepts a static ID or a handlebars expression such as `{{record.Attachment_Id__c}}` to resolve the file at runtime — dynamic IDs require the export to run as a lookup (`isLookup: true`). The ID must belong to the object named in `sObjectType`."},"includeDeletedRecords":{"type":"boolean","description":"When true, SOQL exports use Salesforce's `queryAll()` instead of `query()`, including Recycle Bin records deleted within the past 15 days; each record's `IsDeleted` field identifies deletions. Useful for synchronizing deletes to target systems or maintaining a complete audit trail. Ignored for distributed and blob exports.","default":false},"api":{"type":"string","description":"Salesforce API used to run SOQL queries; ignored for distributed and blob exports. Use `rest` (the default when omitted) for smaller datasets (under 10,000 records) and for lookup exports — the Bulk API is not compatible with `isLookup: true`. Use `bulk` for large data volumes and higher throughput, optionally tuned through the `bulk` object.","enum":["rest","bulk"]},"bulk":{"type":"object","description":"Settings for Salesforce Bulk API 2.0 jobs. Only applies when `api` is `bulk` on a SOQL export; ignored otherwise.","properties":{"maxRecords":{"type":"integer","description":"Caps how many records a single Bulk API job retrieves, which helps prevent timeouts with complex queries or large records. When omitted, Salesforce's default applies. Lower values suit complex or custom objects; higher values improve throughput for simple records.","minimum":10000},"purgeJobAfterExport":{"type":"boolean","default":true,"description":"When true, deletes the Bulk API job in Salesforce after all data is retrieved, keeping the Bulk Data Load Jobs list clean — at the cost of making job details unavailable for later troubleshooting. Has no effect on the data retrieved or the success of the export."}}},"soql":{"type":"object","description":"SOQL query configuration; required when `type` is `soql` and not used otherwise. The query controls which objects, fields, and filter conditions the export retrieves, with either the REST or Bulk API.\n\nRequired when type is soql.","properties":{"query":{"type":"string","description":"SOQL statement passed directly to Salesforce that defines which objects, fields, and records the export retrieves. Supports handlebars — for delta exports, filter with `WHERE LastModifiedDate > {{lastExportDateTime}}` — and relationship queries that fetch parent and child records together. Select only the fields you need and use `ORDER BY` for consistent results across pages.","maxLength":200000}}},"distributed":{"type":"object","description":"Real-time listener configuration — the object that makes an export a listener. Set it, with `type: distributed`, when creating one; a listener saved from the integrator.io form carries it with no `type`, and the platform fires it either way. Not used on SOQL or file exports. Records reach the flow only when an Apex trigger on `sObjectType` in the connected org calls the Celigo package (`integrator_da__.RealTimeExporter.processExport()`) — no scheduling or manual execution is involved. Integration Apps create their triggers at installation; for a custom flow the customer deploys the trigger in Salesforce, starting from the code the export page shows under Required trigger.","properties":{"referencedFields":{"type":["array","null"],"description":"Fields from related objects to include in the exported record, written in dot notation (e.g. `Account.Name`, `Owner.Email`, `Custom_Lookup__r.Field__c`). Works with lookup and master-detail relationships, up to 10 unique referenced relationships per export. Referenced fields are retrieved via separate API calls, so include only fields the integration actually needs.","items":{"type":["string","null"]}},"disabled":{"type":"boolean","description":"When true, the listener stops processing events while its configuration and Salesforce triggers stay in place. Events that occur while disabled are ignored, not queued — they are not processed retroactively when re-enabled, so consider a delta export to catch up after extended pauses."},"qualifier":{"type":["string","null"],"description":"SOQL WHERE-clause condition that filters which record events the listener processes; when omitted (stored as `null`), every event for the object is processed. The Celigo Salesforce package evaluates it inside Salesforce as `SELECT Id FROM <sObjectType> WHERE (<qualifier>) AND Id IN :ids` before anything is sent, so only fields of the monitored object and SOQL comparison syntax apply — Salesforce formula functions are not part of this grammar. A deleted record is never returned by that query, so a listener with any qualifier receives no delete events; leave it unset when removals must trigger the flow."},"batchSize":{"type":["integer","null"],"description":"Controls how many event records are grouped into each real-time processing batch. Stored as `null` when not configured. Smaller batches lower latency for time-sensitive operations; larger batches improve throughput for high-volume objects. Does not limit how many records are processed in total — only how they are grouped.","minimum":4,"maximum":200},"skipExportFieldId":{"type":["string","null"],"description":"API name of a Salesforce checkbox field used to prevent infinite loops in bidirectional syncs: when the integration updates a record, this field is set so the resulting event is ignored, then cleared automatically. Required for bidirectional sync scenarios. The field must be a checkbox dedicated to integration use and updateable by the integration user."},"relatedLists":{"type":["array","null"],"description":"Child record sets to include with the parent record when it changes, one entry per related list. When omitted (stored as `null`), only the primary record is processed. Each related list adds Salesforce API calls and increases payload size.","items":{"type":"object","description":"Defines how to retrieve one type of child record related to the primary object. Configure multiple entries to retrieve different types of children.","properties":{"referencedFields":{"type":"array","description":"API names of the child object fields to retrieve; only the listed fields are included, and an empty array retrieves only the `Id` field. Include only fields the integration needs — each field adds data volume and processing time.","items":{"type":["string","null"]}},"parentField":{"type":"string","description":"API name of the lookup or master-detail field on the child object that references the parent (e.g. `AccountId`, `Parent_Object__c`) — the field's API name, not the relationship name. Used to build the query that fetches children for each parent record."},"sObjectType":{"type":"string","description":"Case-sensitive API name of the child object to retrieve (e.g. `Contact`, `Custom_Child__c`) — use the API name, not the label. The object must have a relationship field to the parent and be accessible to the connected user."},"filter":{"type":"string","description":"SOQL condition that limits which child records are included; when omitted, all related children are retrieved. Provide only the condition expression without the `WHERE` keyword — it is combined automatically with the parent relationship filter."},"orderBy":{"type":"string","description":"Sort order for the retrieved child records, as field names with optional `ASC`/`DESC` directions (e.g. `CreatedDate DESC`). Provide only the fields and directions without the `ORDER BY` keywords. When omitted, Salesforce determines the order."}},"required":["parentField","referencedFields","sObjectType"]}}}}},"if":{"properties":{"type":{"const":"distributed"}},"required":["type"]},"then":{"required":["distributed","sObjectType"]},"else":{"if":{"required":["distributed"]},"then":{},"else":{"if":{"required":["id"]},"then":{"required":["sObjectType"]},"else":{"if":{"not":{"propertyNames":{"enum":["metadata"]}}},"then":{"required":["soql","type"]}}}}},"AS2":{"type":"object","description":"Configures AS2 exports and listeners. Optional — the transport settings live on the AS2\nconnection, and an AS2 export is valid with only `file` (parse settings); set this object\nto link a Trading Partner Connector or to transfer raw files as blobs. An AS2 listener\nacts as the flow's source, receiving trading-partner transmissions in near real-time and\nhandling decryption, signature verification, and MDN generation; AS2 (Applicability\nStatement 2) transmits EDI and other data securely over HTTP/S using S/MIME encryption\nand digital signatures.","properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies the partner-specific EDI configuration —\ncommunication protocol, document schemas, mappings, validation rules, and endpoint\ndetails. Set this to link the export to all settings required for AS2 communication\nwith that partner."},"blob":{"type":"boolean","description":"When true, retrieves raw files without parsing them into structured records (rendered as\na \"Transfer\" step in the flow UI). Use only when the file contents are not needed in\nsubsequent steps — for binary files or when parsing is handled downstream. Only\navailable on AS2 and VAN exports."}},"required":[]},"DynamoDB":{"type":"object","description":"Defines how records are queried from DynamoDB tables. Required when the _connectionId field\nreferences a DynamoDB connection; must not be included for other connection types. Basic\nexports need region, method, tableName, keyConditionExpression, expressionAttributeNames,\nand expressionAttributeValues; once exports (export type \"once\") additionally need\nonceExportPartitionKey, plus onceExportSortKey for composite-key tables.","required":["region","tableName","keyConditionExpression","expressionAttributeNames","expressionAttributeValues"],"properties":{"region":{"type":"string","enum":["us-east-1","us-east-2","us-west-1","us-west-2","af-south-1","ap-east-1","ap-south-1","ap-northeast-1","ap-northeast-2","ap-northeast-3","ap-southeast-1","ap-southeast-2","ca-central-1","eu-central-1","eu-west-1","eu-west-2","eu-west-3","eu-south-1","eu-north-1","me-south-1","sa-east-1"],"description":"AWS region hosting the DynamoDB table. Must match the region where the table is\ndeployed so the integration can reach it.","default":"us-east-1"},"method":{"type":"string","enum":["query"],"description":"DynamoDB operation used to retrieve items. Only \"query\" is currently supported."},"tableName":{"type":"string","description":"Name of the DynamoDB table to query. Must exactly match an existing table (case-sensitive)."},"keyConditionExpression":{"type":"string","description":"Key condition determining which items the query retrieves. Must include a condition on\nthe partition key and may add sort-key conditions (equality, BETWEEN, begins_with).\nReference attribute names with \"#\" placeholders defined in expressionAttributeNames and\nvalues with \":\" placeholders defined in expressionAttributeValues."},"filterExpression":{"type":"string","description":"Filters query results on non-key attributes after the key condition is applied; omit to\nreturn all items matching the key condition. Uses the same \"#\" and \":\" placeholders\ndefined in expressionAttributeNames and expressionAttributeValues."},"projectionExpression":{"type":"array","items":{"type":"string"},"description":"Attributes to return from each item, reducing data transfer; omit to return all\nattributes. Each array element is one field, referenced via \"#\" placeholders defined in\nexpressionAttributeNames."},"expressionAttributeNames":{"type":"string","description":"JSON string mapping \"#\" placeholders to actual attribute names, e.g.\n{\"#pk\": \"customerId\"}. Placeholders defined here are used in keyConditionExpression,\nfilterExpression, and projectionExpression."},"expressionAttributeValues":{"type":"string","description":"JSON string mapping \":\" placeholders to comparison values, e.g. {\":status\": \"ACTIVE\"}.\nValues can be static or dynamic handlebars expressions such as {{lastExportDateTime}},\nand are referenced from keyConditionExpression and filterExpression."},"onceExportPartitionKey":{"type":"string","description":"Partition key attribute that uniquely identifies items when the export's type is \"once\".\nCeligo uses it to mark items as processed after a successful export, preventing the same\nitems from being exported again on subsequent runs."},"onceExportSortKey":{"type":"string","description":"Sort key attribute used together with onceExportPartitionKey to identify processed items\nwhen the table has a composite primary key. Omit for tables keyed by a partition key\nalone."},"pathToRecords":{"$ref":"#/components/schemas/PathToRecords"},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"}}},"PathToRecords":{"type":"string","maxLength":250,"description":"Path to the array of records inside each document or item fetched from the source; each array element is emitted as an individual record, which keeps records within the 5 MB page-size limit even when the source document exceeds it. Accepts dot notation (e.g. `data.orders`) or JSONPath for values starting with `$`, including unions (`$['orders','invoices'][*]`) and filter expressions (`$.items[?(@.qty>3)]`); a path that resolves to nothing emits zero records for that document or item. When omitted, each document or item is emitted as one record, exactly as before — HTTP and file-based exports configure this same capability through their existing `http.response.resourcePath` and `file.json`/`file.xml` `resourcePath` fields."},"FTP":{"type":"object","description":"Defines which files to retrieve from an FTP, FTPS, or SFTP server. Required when the\n_connectionId field references an FTP/SFTP connection; must not be included for other\nconnection types. directoryPath selects the folder, fileNameStartsWith/fileNameEndsWith\nfilter files by name, and backupDirectoryPath controls where files are moved after\nretrieval.","required":["directoryPath"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies partner-specific B2B settings for this export.\nWhen set, the export inherits the connector's pre-configured settings; omit to use only\nthe FTP connection details."},"directoryPath":{"type":"string","description":"Directory on the server to retrieve files from, either absolute or relative to the login\ndirectory; the FTP user must have read permission on it. Use forward slashes regardless\nof server OS — paths are case-sensitive on UNIX/Linux servers. Supports handlebars\ntemplates, e.g. archive/{{date 'YYYY-MM-DD'}}."},"fileNameStartsWith":{"type":"string","description":"Only retrieves files whose names start with this value (case-sensitive on most servers);\naccepts static text or handlebars templates. When combined with fileNameEndsWith, files\nmust match both."},"fileNameEndsWith":{"type":"string","description":"Only retrieves files whose names end with this value, commonly a file extension\n(case-sensitive on most servers). When combined with fileNameStartsWith, files must\nmatch both."},"backupDirectoryPath":{"type":"string","description":"Directory on the same server where files are moved after successful export, giving you\nan independent backup; if omitted, files are simply deleted from the source directory\nafter successful export unless `file.skipDelete` is true (Celigo also keeps its own copy\nof processed files for a set period). Supports static paths or handlebars templates."}}},"JDBC":{"type":"object","description":"Configuration object for JDBC (Java Database Connectivity) data integration exports.\n\nThis object is REQUIRED when the _connectionId field references a JDBC database connection\nand must not be included for other connection types. It defines how data is extracted\nfrom relational databases using SQL queries.\n\n**Jdbc export capabilities**\n- Execute custom SQL SELECT statements\n- Support for joins, aggregations, and functions\n- Flexible data retrieval from any accessible tables or views\n- Compatible with all major database systems\n**Critical:** WHAT BELONGS IN THIS OBJECT\n- `query` - **ALWAYS REQUIRED** - The SQL SELECT statement\n- `once` - **REQUIRED** when the export's Object Type is `\"once\"` (set _include_once: true)\n- **DO NOT** put `delta` inside this object - delta is handled via the query\n\n**Delta exports (type: \"delta\")**\nFor delta/incremental exports, do NOT populate a `delta` object inside `jdbc`.\nInstead, use `{{lastExportDateTime}}` or `{{currentExportDateTime}}` directly in the query:\n```json\n{\n  \"type\": \"delta\",\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE updatedAt > {{lastExportDateTime}}\"\n  }\n}\n```\n\n**Once exports (type: \"once\")**\nFor once exports (mark records as processed), populate `jdbc.once.query`:\n```json\n{\n  \"type\": \"once\",\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE exported = false\",\n    \"once\": {\n      \"query\": \"UPDATE customers SET exported = true WHERE id = {{record.id}}\"\n    }\n  }\n}\n```\n\n**Standard exports (type: null or not specified)**\nJust provide the query:\n```json\n{\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE status = 'ACTIVE'\"\n  }\n}\n```","required":["query"],"properties":{"formType":{"type":["string","null"],"enum":["sql","simple",null],"description":"Selects how the export's query is authored. Use `sql` for a hand-written SQL statement in\n`query`; use `simple` to build the query from the `simple` object's table/columns/filter."},"query":{"type":"string","description":"SQL SELECT statement executed to retrieve data, from simple table selections to joins and\naggregations. For delta exports, include {{lastExportDateTime}}/{{currentExportDateTime}}\nin the WHERE clause rather than configuring a separate delta object."},"once":{"type":"object","description":"**CRITICAL: REQUIRED when the export's Object Type is \"once\".**\n\nIf Object Type is \"once\", you MUST set _include_once to true (or include this object).\n\nThis object has ONLY ONE property: \"query\" (a SQL UPDATE string).\nDO NOT create any other properties like \"update\", \"table\", \"set\", \"where\", etc.\n\nCORRECT format:\n```json\n{\"query\": \"UPDATE customers SET exported=true WHERE id={{record.id}}\"}\n```\n\nWRONG format (DO NOT DO THIS):\n```json\n{\"update\": {\"table\": \"customers\", \"set\": {...}}}\n```\n","properties":{"query":{"type":"string","description":"**REQUIRED** - A SQL UPDATE statement string to mark records as processed.\n\nThis is a plain SQL UPDATE query string, NOT a structured object.\n\nThe query runs AFTER each record is successfully exported, setting a flag\nto indicate the record has been processed.\n\nFormat: \"UPDATE <table> SET <column>=<value> WHERE <id_column>={{record.<id_field>}}\"\n\nExample: \"UPDATE customers SET exported=true WHERE id={{record.id}}\"\n\nThe {{record.id}} placeholder is replaced with the actual record ID from each exported row.\n"}}},"simple":{"type":"object","description":"Visual query-builder configuration used when `formType` is `simple`; the platform builds\nthe SELECT from these instead of raw SQL. Only relevant when `formType` is `simple`.","properties":{"table":{"type":"string","description":"Table or view to select rows from."},"columns":{"type":"array","items":{"type":"string"},"description":"Columns to return; an empty list selects all columns."},"filter":{"description":"Filter applied as the WHERE clause for the generated query, stored as the\nplatform's structured filter-rules object.","allOf":[{"$ref":"#/components/schemas/Filter"}]}}}}},"MongoDB":{"type":"object","description":"Defines how documents are retrieved from MongoDB collections. Required, with `method` and\n`collection`, when the _connectionId field references a MongoDB connection and the export\nfetches documents; must not be included for other connection types. A change-stream\nlistener (`type: stream`) is configured through `cdc` instead and may omit this object, or\nset only `collection` to scope the listener. Supports find queries with optional filter\ncriteria and field projections.","properties":{"method":{"type":"string","enum":["find"],"description":"MongoDB operation used to retrieve documents. Only \"find\" is currently supported,\nequivalent to db.collection.find(filter, projection)."},"collection":{"type":"string","description":"Name of the MongoDB collection to query. Case-sensitive and must reference an existing\ncollection in the connected database. Required on find exports; on a change-stream\nlistener (`type: stream`) it is optional and may list several collections separated by\ncommas, matching the listener form's \"Collections\" field."},"filter":{"type":"string","description":"MongoDB query document, as a JSON string, that selects which documents to export; omit\nto return every document in the collection. Supports standard MongoDB query operators\nand handlebars variables for dynamic values — for example\n{\"lastModified\": {\"$gt\": \"{{lastRun}}\"}} for incremental processing."},"projection":{"type":"string","description":"MongoDB projection document, as a JSON string, that limits which fields are returned;\nomit to return all fields. Use 1 to include fields or 0 to exclude them — the two modes\ncannot be mixed except for _id, which is included by default unless explicitly excluded.\nProjection affects only the shape of returned documents, not which documents match."},"pipeline":{"type":"string","description":"MongoDB aggregation pipeline, as a JSON-array string of stages, used instead of a plain\nfind when documents need to be transformed, joined, or grouped server-side (e.g. `$match`,\n`$lookup`, `$group`). When set, it takes the place of `filter`/`projection`. Supports\nhandlebars variables for incremental processing."},"readPreference":{"type":"string","enum":["doNotOverride","primary","primaryPreferred","secondary","secondaryPreferred","nearest"],"default":"doNotOverride","description":"Overrides which replica-set member this export reads from, trading consistency for load\ndistribution. Leave at `doNotOverride` to inherit the connection's setting."},"pathToRecords":{"$ref":"#/components/schemas/PathToRecords"},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"}}},"NetSuite":{"type":"object","description":"NetSuite-specific export configuration. Required when `_connectionId` references a NetSuite\nconnection; omit for all other connection types. Supports saved-search, RESTlet, and\ndistributed (SuiteApp) exports, plus two file cabinet modes: blob exports transfer files\nas-is (set the export's top-level `type` to `blob` and configure `netsuite.blob`), while\nfile exports parse file contents into records (leave the export's top-level `type` unset\nand configure `netsuite.file`).","properties":{"type":{"type":"string","enum":["search","basicSearch","metadata","selectoption","restlet","getList","getServerTime","distributed","file"],"description":"Controls how data is retrieved from NetSuite and which sibling object must be configured:\n`search` pairs with `netsuite.searches`, `restlet` with `netsuite.restlet`, `distributed`\nwith `netsuite.distributed`, and `file` with `netsuite.file.folderInternalId`. For blob\nexports (raw file transfer without parsing), leave this field unset, set the export's\ntop-level `type` to `blob`, and configure `netsuite.internalId` instead. For lookups\n(`isLookup: true`), prefer `restlet`, which supports `suiteapp2.0` saved searches with\ndynamic inputs; `search` is limited for dynamic lookups."},"searches":{"type":"array","description":"Saved search configurations that query NetSuite for records. Each entry references a\nsaved search by internal ID, names the record type being searched, and can add filter\ncriteria.","items":{"type":"object","properties":{"savedSearchId":{"type":"string","description":"Internal ID of the NetSuite saved search to run."},"recordType":{"type":"string","description":"NetSuite record type being searched, as its exact lowercase script ID\n(e.g. \"customer\", \"salesorder\") — not the display name."},"criteria":{"type":"array","description":"Additional filter criteria applied on top of the saved search.","items":{"type":"object","properties":{"field":{"type":"string","description":"Script-id of the search column to filter on.  REQUIRED.\nMust come from the record type's SEARCH COLUMNS list\n(NOT the body-fields list).  Becomes the `name`\nargument of NetSuite's `search.createFilter()`."},"operator":{"type":"string","description":"Comparison operator.  REQUIRED.  Supported values\ndepend on the column's data type (e.g. `is`,\n`isnot`, `after`, `before`, `anyof`,\n`noneof`, `contains`, `startswith`,\n`greaterthan`, `lessthan`).  Must be a string\nNetSuite recognises -- mismatched operators cause\nruntime rejection."},"join":{"type":"string","description":"Optional join name when filtering through a related\nrecord (e.g. `customer` from a sales order).  Use\nonly when the column lives on a related record and\nNetSuite supports the join from the base record type."},"searchValue":{"description":"Value (or array of values for `anyof`-style\noperators) to compare against the column.  Required\nfor most operators; omit for the few unary operators\n(e.g. `isempty`, `isnotempty`)."}},"required":["field","operator"]}}}}},"metadata":{"type":"object","description":"Supplemental metadata associated with the NetSuite export."},"selectoption":{"type":"object","description":"Configuration for `selectoption` exports, which retrieve the available select options\nfor a NetSuite field."},"customFieldMetadata":{"type":"object","description":"Metadata describing the custom fields defined in the NetSuite account."},"skipGrouping":{"type":"boolean","description":"When true, each saved-search result row becomes its own record. When false or omitted,\nconsecutive result rows that share the record's internal id are grouped into one record\nshaped as an array of rows — the *Group rows* option in the UI."},"statsOnly":{"type":"boolean","description":"When true, returns only summary statistics about matching records instead of the\ndetailed records themselves."},"internalId":{"type":"string","description":"Internal ID of the file in the NetSuite file cabinet to export. Required for blob\nexports (the export's top-level `type` is `blob`); the file is transferred as-is\nwithout parsing. For parsed file exports, use `netsuite.file.folderInternalId` instead."},"blob":{"type":"object","properties":{"purgeFileAfterExport":{"type":"boolean","description":"When true, permanently deletes the file from the NetSuite file cabinet after a\nsuccessful export. When false or omitted, the file is left in place."}},"description":"Configuration for blob exports, which transfer files from the NetSuite file cabinet\nas-is without parsing them into records. To export a blob, set the export's top-level\n`type` to `blob`, set `netsuite.internalId` to the file's internal ID, and leave\n`netsuite.type` unset. Use `netsuite.file` instead when file contents should be parsed\ninto records."},"restlet":{"type":"object","properties":{"recordType":{"type":"string","description":"NetSuite record type the RESTlet operates on, as its script ID. Custom record\ntypes use the \"customrecord_\" prefix."},"batchSize":{"type":"number","description":"Number of records the RESTlet returns per request (default 1000). This, not the\nexport's `pageSize`, sizes each NetSuite call; the export keeps requesting until the\nsearch is exhausted. Larger batches reduce round-trips but raise NetSuite governance\nand memory use per call; tune to the record's size. The RESTlet caps a call at 1000,\nso a larger value acts as 1000. In a test run or preview the request is capped at the\nexport's `pageSize` (default 20) instead."},"searchId":{"type":["string","null"],"description":"Internal ID of the NetSuite saved search the RESTlet executes. Results follow the\nsaved search's current definition, so editing the search in NetSuite changes the\nexport output without changing this ID."},"useSS2Restlets":{"type":"boolean","description":"When true, calls SuiteScript 2.0 RESTlets. When false or omitted, legacy\nSuiteScript 1.0 RESTlets are used.\nSet at step creation together with restletVersion — the same\ncreation-time-only constraint applies (see restletVersion)."},"restletVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"RESTlet version the export invokes. Defaults to `suiteapp2.0` when\n`useSS2Restlets` is true, `suitebundle` otherwise.\nThe version is fixed when the step is created — the Advanced selector is\ndisabled on existing steps, and migrating an existing export to a\ndifferent version means recreating or cloning it."},"criteria":{"type":"array","description":"Filter conditions added on top of the saved search (the UI's \"Additional search\ncriteria\"). Each condition pairs a search column with an operator and one or two\ncomparison values, and the conditions are combined with AND — every one must match.","items":{"type":"object","properties":{"field":{"type":"string","description":"Script-id of the search column to filter on. This is the primary\nfield the criterion is evaluated against (e.g. \"trandate\", \"status\")."},"join":{"type":"string","description":"Join relationship that applies this filter to a related record's fields instead\nof the base record. Must match a join name NetSuite defines for the record type."},"operator":{"type":"string","enum":["after","allof","any","anyof","before","between","contains","doesnotcontain","doesnotstartwith","equalto","greaterthan","greaterthanorequalto","haskeywords","is","isempty","isnot","isnotempty","lessthan","lessthanorequalto","noneof","notafter","notallof","notbefore","notbetween","notequalto","notgreaterthan","notlessthan","notlessthanorequalto","noton","notonorafter","notonorbefore","notwithin","on","onorafter","onorbefore","startswith","within"],"description":"Comparison operator applied between the column and the search value, using\nNetSuite's lowercase search operator names. Which operators a column accepts\ndepends on its data type."},"searchValue":{"description":"Value the column is compared against — a string, a number, or an array of\nvalues for `anyof`-style operators. On lookup steps it may be a Handlebars\nexpression rendered from the incoming record (for example `{{poNumber}}`)."},"searchValue2":{"description":"Second comparison value for the two-value operators `between` and `notbetween`\n(the upper bound). Omit for every other operator."},"formula":{"type":"string","description":"NetSuite formula expression used as the filter condition, for criteria that\nstandard field-operator-value comparisons can't express."}}}},"columns":{"type":"array","description":"Search columns the RESTlet returns for each matching record. With a saved search,\nthe RESTlet returns the search's own result columns followed by any listed here: a\nlist adds columns and never removes one of the search's, so remove a column in the\nsaved search itself. An ad-hoc search (no `searchId`) must list its columns here.","items":{"type":"object","properties":{"_id":{"type":"string","readOnly":true,"description":"Subdocument id the server assigns to the column entry on save. Not a NetSuite\nfield id — never set it when authoring."},"name":{"type":"string","description":"Column field name as referenced in the search."},"join":{"type":"string","description":"Join relationship that sources this column from a related record instead of\nthe base record. Supports dot notation for nested joins (e.g. \"employee.manager\")."},"summary":{"type":"string","enum":["group","sum","count","avg","min","max"],"description":"Summary type applied to this column, turning the search into a summary search."},"formula":{"type":"string","description":"NetSuite formula expression that computes this column's value at runtime, such\nas a CASE statement or date formatting, instead of reading a stored field."},"label":{"type":"string","description":"Display name for the column; it becomes the property name of the column's value\nin each exported record."},"sort":{"type":"boolean","description":"When true, query results are sorted by this column's values."}}}},"markExportedBatchSize":{"type":"integer","description":"Number of records updated per RESTlet call when marking records as exported in\nNetSuite (live: an integer count, e.g. 100).","maximum":100,"minimum":1},"hooks":{"type":["object","null"],"properties":{"batchSize":{"type":"number","description":"Legacy location of the RESTlet page size; `restlet.batchSize` is the current\nfield and takes precedence when both are set."},"preSend":{"type":"object","properties":{"fileInternalId":{"type":"string","description":"Internal ID of the JavaScript file in the NetSuite File Cabinet that defines\nthe hook function. Must be readable by the role behind the export's connection."},"function":{"type":"string","description":"Name of the global function in that file that NetSuite invokes as the pre\nsend hook."},"configuration":{"type":"object","description":"Free-form JSON stored with the hook reference for the script's own use. Custom\nsettings in scope for the export are passed to the function separately as\n`options.settings`."}},"description":"SuiteScript hook that runs inside NetSuite after a page of records is collected\nand before it is sent to the Celigo platform."}},"description":"SuiteScript hooks for the export. Only `preSend` exists on exports; imports carry\nthe pre map, post map, and post submit hooks."},"cLocked":{"type":"object","description":"Lock state that prevents modification of the configuration when set."}},"description":"Configuration for `restlet` exports. Identifies the RESTlet script that retrieves data\nfrom NetSuite, along with the saved search, criteria, and columns it executes with."},"distributed":{"type":"object","properties":{"recordType":{"type":"string","description":"NetSuite record type the listener is deployed on, as its lowercase script ID\n(`customer`, `salesorder`, `customrecord_<scriptid>`) — never the display name or a\ncustom record's numeric internal ID. Types the platform's record-type catalog marks\n`doesNotSupportRTExport` cannot host a listener and are not offered in the UI."},"executionContext":{"type":"array","description":"NetSuite execution contexts that trigger this distributed export. A record change\nfires the export only when it occurs in one of the listed contexts.","default":["userinterface","webstore"],"items":{"type":"string","enum":["userinterface","webservices","csvimport","offlineclient","portlet","scheduled","suitelet","custommassupdate","workflow","webstore","userevent","mapreduce","restlet","webapplication","restwebservices"]}},"disabled":{"type":"boolean","description":"When true, the listener's NetSuite deployment is inactive and record saves no longer\ntrigger the export. Maintained by the platform as the flow is disabled and re-enabled;\nre-enabling the flow re-registers the deployment. A listener is created disabled, and\na flow created already enabled leaves it that way until a flow update sets\n`disabled: false`, which activates it within seconds."},"executionType":{"type":"array","description":"Record operations that trigger this distributed export. A record event fires the\nexport only when its operation matches one of the listed types.","default":["create","edit","xedit"],"items":{"type":"string","enum":["create","edit","delete","xedit","copy","view","cancel","approve","reject","pack","ship","markcomplete","reassign","editforecast","dropship","specialorder","orderitems","paybills","print","email"]}},"qualifier":{"type":["array","string","null"],"description":"Field-specific qualification criteria the SuiteApp evaluates against the record before\nexporting it; saves that do not match are discarded. Stored as a filter-expression array\nof `[field, operator, value]` rules joined by lowercase `\"and\"` / `\"or\"`, as a legacy\nexpression string, or null when no criteria are set."},"skipExportFieldId":{"type":"string","description":"Script ID of a checkbox body field on the record type. A save with that box checked is\nskipped by the listener, which then clears the box itself — used to keep a mass update\nfrom fanning out and to stop a write-back import from re-triggering the listener."},"hooks":{"type":"object","description":"SuiteScript hook that runs inside NetSuite before the record leaves it.","properties":{"preSend":{"type":"object","description":"Pre-send hook: a SuiteScript function in the NetSuite file cabinet invoked before\nrecords leave NetSuite, with the full SuiteScript API available — for example to run\ndynamic searches against the record being exported.","properties":{"fileInternalId":{"type":"string","description":"Internal ID of the file cabinet file that holds the hook's SuiteScript."},"function":{"type":"string","description":"Name of the function in that file to invoke for each export."},"configuration":{"type":"object","description":"Settings passed to the hook function."}}}}},"sublists":{"type":["array","null"],"description":"IDs of the NetSuite sublists to include with each exported record (for example `item`\nfor transaction line items). Records are exported with body fields only when no\nsublists are selected. Legacy documents may store null (equivalent to no sublists).","items":{"type":"string"}},"referencedFields":{"type":"object","description":"Field references recorded on the listener by the platform. Not exposed in the listener\nform; leave as stored."},"relatedLists":{"type":"object","description":"Related record lists recorded on the listener by the platform. Not exposed in the\nlistener form; leave as stored."},"forceReload":{"type":"boolean","description":"When true, the listener reloads the record from the database after the save before\nexporting it, so fields that are populated only once the save completes — such as\ntransaction line IDs on new lines — are included. The reload slows every save of that\nrecord type."},"ioEnvironment":{"type":"string","description":"integrator.io environment the listener's NetSuite deployment reports to. Written by the\nplatform when the listener registers with NetSuite; live-observed values include\n`production` and `staging`."},"ioDomain":{"type":"string","description":"integrator.io domain the listener's NetSuite deployment calls back to — the account's\nown regional domain, stamped by the platform on every save and echoed on create and\nupdate responses; not returned by GET."},"lastSyncedDate":{"type":"string","format":"date-time","description":"When the listener's deployment last synchronized with NetSuite. Written by the platform;\nnot set until the first successful synchronization."},"settings":{"type":"object","description":"Additional key-value settings for the distributed export. Not exposed in the listener\nform."},"useSS2Framework":{"type":"boolean","description":"Derived by the platform from `frameworkVersion` — true when it is `suiteapp2.0`, absent\notherwise — and echoed on create and update responses; not returned by GET. A value\nsent on create or update is replaced by the derived one."},"frameworkVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"SuiteApp framework version used by the distributed export.\nFixed when the step is created — the Advanced selector is disabled on\nexisting steps, and migrating an existing listener to a different\nversion means recreating or cloning it."}},"description":"Configuration for `distributed` exports, which use the NetSuite SuiteApp to fire\nreal-time, event-driven exports when records change. Define the record type to listen\nto and the execution contexts and operations that trigger it."},"getList":{"type":"array","description":"Configuration for `getList` exports — a list of record references, each retrieving one\nNetSuite record by its identifier. The export returns the fetched records in list order.","items":{"type":"object","properties":{"type":{"type":"string","description":"Standard NetSuite record type to retrieve (e.g. \"customer\", \"salesOrder\").\nUsed when `typeId` is absent. Case-sensitive."},"typeId":{"type":"string","description":"Script ID of a custom record type (\"customrecord_...\") or custom transaction\ntype (\"customtransaction_...\"). Takes precedence over `type` when present.\nCase-sensitive."},"internalId":{"type":"string","description":"Internal ID of the NetSuite record to retrieve. Assigned by NetSuite at record\ncreation and unique within the record type. Takes precedence over `externalId`."},"externalId":{"type":"string","description":"Identifier assigned by an external system, used to reference and synchronize the\nrecord across systems. Distinct from the NetSuite-assigned internal ID."}}}},"searchPreferences":{"type":"object","properties":{"bodyFieldsOnly":{"type":"boolean","description":"When true, search results include only the record's body fields and exclude fields\nfrom joined or related records. Reduces payload size when related data isn't needed."},"pageSize":{"type":"number","description":"Number of search results NetSuite returns per page."},"returnSearchColumns":{"type":"boolean","description":"When true, search results include the column data defined in the search. When\nfalse, column data is omitted and results contain only minimal record information."}},"description":"Preferences that control how NetSuite executes searches and shapes the results it returns."},"file":{"type":"object","description":"Configuration for file exports, which retrieve files from the NetSuite file cabinet\nand parse their contents (CSV, XML, JSON) into records. Leave the export's top-level\n`type` unset when using this object; for raw transfers without parsing, use\n`netsuite.blob` with the export's `type` set to `blob` instead.","properties":{"folderInternalId":{"type":"string","description":"Internal ID of the NetSuite file cabinet folder to export files from. Accepts a\nhandlebars expression (e.g. `{{record.folderId}}`) when the folder must be\nselected dynamically from record data. The ID is stable across folder renames and\nmoves, but may differ between non-production and production accounts."},"backupFolderInternalId":{"type":"string","description":"Internal ID of the NetSuite file cabinet folder where backup files are stored.\nIDs are account-specific and do not transfer between non-production and production\nenvironments."},"fileNameStartsWith":{"type":"string","description":"Optional prefix filter. Only files whose names start with this string are\nexported from the configured folder."},"fileNameEndsWith":{"type":"string","description":"Optional suffix filter. Only files whose names end with this string (e.g. a\n\".csv\" extension) are exported from the configured folder."}}}},"required":[]},"RDBMS":{"type":"object","description":"Configuration object for Relational Database Management System (RDBMS) data integration exports.\n\nThis object defines how data is read from a relational database and must not be included\nfor other connection types. For query-type exports (standard, delta, once) it is REQUIRED\nwhen the _connectionId field references an RDBMS database connection and holds the SQL\nquery. For real-time stream listeners (`type: \"stream\"`) it holds the CDC watch list\n(`tables`) instead — and a PostgreSQL listener scoped by a publication\n(`cdc.publicationName`) may omit this object entirely.\n\n**Rdbms export capabilities**\n- Execute custom SQL SELECT statements\n- Support for joins, aggregations, and functions\n- Flexible data retrieval from any accessible tables or views\n- Compatible with all major database systems\n\n**Critical:** WHAT BELONGS IN THIS OBJECT\n- `query` - The SQL SELECT statement - required for every query-type export (standard, delta, once)\n- `once` - **REQUIRED** when the export's Object Type is `\"once\"` (set _include_once: true)\n- `tables` - the CDC listener watch list - real-time stream listeners (`type: \"stream\"`) only\n- **DO NOT** put `delta` inside this object - delta is handled via the query\n- **DO NOT** author `query` on a stream listener - real-time CDC exports have no SQL query\n\n**Delta exports (type: \"delta\")**\nFor delta/incremental exports, do NOT populate a `delta` object inside `rdbms`.\nInstead, use `{{lastExportDateTime}}` or `{{currentExportDateTime}}` directly in the query, and\nset the export's top-level `delta.startDate`: without it the first run's window is empty.\n```json\n{\n  \"type\": \"delta\",\n  \"delta\": { \"startDate\": \"2026-01-01T00:00:00.000Z\" },\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE updatedAt > {{lastExportDateTime}}\"\n  }\n}\n```\n\n**Once exports (type: \"once\")**\nFor once exports (mark records as processed), populate `rdbms.once.query`:\n```json\n{\n  \"type\": \"once\",\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE exported = false\",\n    \"once\": {\n      \"query\": \"UPDATE customers SET exported = true WHERE id = {{record.id}}\"\n    }\n  }\n}\n```\n\n**Standard exports (type: null or not specified)**\nJust provide the query:\n```json\n{\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE status = 'ACTIVE'\"\n  }\n}\n```\n\n**Real-time CDC listener exports (type: \"stream\")**\nSQL Server and PostgreSQL connections support change-data-capture listeners that stream row\nchanges continuously (the `cdc` object holds the listener settings). A stream export has NO\n`query` — it watches `tables`:\n```json\n{\n  \"type\": \"stream\",\n  \"rdbms\": { \"tables\": \"public.orders,public.customers\" },\n  \"cdc\": { \"properties\": [{ \"name\": \"snapshot.mode\", \"value\": \"no_data\" }] }\n}\n```\nA PostgreSQL listener can instead scope tables through a publication\n(`cdc.publicationName`), in which case `tables` stays unset and the `rdbms` object may be\nomitted entirely.","properties":{"tables":{"type":"string","description":"Comma-separated list of tables the CDC listener watches for changes. Used only by real-time\nlistener exports (`type` is `stream`); standard SQL exports use `query` instead. SQL Server\nlisteners use fully-qualified three-part names (`database.schema.table`); PostgreSQL\nlisteners use two-part names (`schema.table`)."},"query":{"type":"string","description":"SQL SELECT statement executed to retrieve data, from simple table selections to joins,\naggregations, and parameterized queries with Handlebars expressions. Query-type exports\nonly — real-time stream listeners (`type` is `stream`) have no SQL query. For delta exports,\nreference {{lastExportDateTime}}/{{currentExportDateTime}} directly in the query rather\nthan configuring a separate delta object.\n\nDouble braces render a string as a quoted SQL literal with its apostrophes escaped, and\ntriple braces insert a value raw. Observed in Snowflake queries:\n\n- String helper output (`dateFormat`, `jsonSerialize`) and both delta variables are\n  quoted the same way; a number or boolean renders bare, whether it is a JSON value or a\n  numeric helper's result (`{{add 2 3}}` renders `5`).\n- In a run, {{lastExportDateTime}} and {{currentExportDateTime}} arrive as quoted ISO 8601\n  UTC literals with milliseconds (`'2026-01-15T00:00:00.000Z'`), or in `delta.dateFormat`\n  when it is set: add no quotes of your own, and don't parse them with a format mask\n  written for another format.\n- A preview of an unsaved export document renders both as empty, unquoted text, so a\n  delta query fails to compile there unless the document carries both in `postData`.\n- The quoting leaves backslashes, which Snowflake reads as escape sequences, so a\n  double-brace value holding a backslash arrives changed. Pass such a value in a\n  dollar-quoted constant with triple braces (`$${{{record.field}}}$$`), which Snowflake\n  reads as is, but only when it can't contain `$$`: a `$$` ends the constant, and the rest\n  is read as SQL."},"once":{"type":"object","description":"**CRITICAL: REQUIRED when the export's Object Type is \"once\".**\n\nIf Object Type is \"once\", you MUST set _include_once to true (or include this object).\n\nThis object has ONLY ONE property: \"query\" (a SQL UPDATE string).\nDO NOT create any other properties like \"update\", \"table\", \"set\", \"where\", etc.\n\nCORRECT format:\n```json\n{\"query\": \"UPDATE customers SET exported=true WHERE id={{record.id}}\"}\n```\n\nWRONG format (DO NOT DO THIS):\n```json\n{\"update\": {\"table\": \"customers\", \"set\": {...}}}\n```\n","properties":{"query":{"type":"string","description":"**REQUIRED** - A SQL UPDATE statement string to mark records as processed.\n\nThis is a plain SQL UPDATE query string, NOT a structured object.\n\nThe query runs AFTER each record is successfully exported, setting a flag\nto indicate the record has been processed.\n\nFormat: \"UPDATE <table> SET <column>=<value> WHERE <id_column>={{record.<id_field>}}\"\n\nExample: \"UPDATE customers SET exported=true WHERE id={{record.id}}\"\n\nThe {{record.id}} placeholder is replaced with the actual record ID from each exported row.\n"}}}}},"S3":{"type":"object","description":"Defines which files to retrieve from an Amazon S3 bucket. Required when the _connectionId\nfield references an AWS S3 connection; must not be included for other connection types.\nregion and bucket locate the source, keyStartsWith/keyEndsWith filter objects by key, and\nbackupBucket with keyPrefix controls where files are moved after retrieval.","required":["region","bucket"],"properties":{"region":{"type":"string","default":"us-east-1","description":"AWS region where the bucket is located. Case-insensitive; the value is normalized to\nlowercase."},"bucket":{"type":"string","description":"S3 bucket to retrieve files from. The connection's AWS credentials must have\ns3:ListBucket and s3:GetObject permissions on it."},"keyStartsWith":{"type":"string","description":"Only retrieves objects whose keys start with this value (case-sensitive), effectively\nselecting a folder in S3's flat key structure. When combined with keyEndsWith, objects\nmust match both."},"keyEndsWith":{"type":"string","description":"Only retrieves objects whose keys end with this value (case-sensitive), commonly a file\nextension. When combined with keyStartsWith, objects must match both."},"backupBucket":{"type":"string","description":"Bucket in the same region where files are moved after successful export, giving you an\nindependent backup; if omitted, files are simply deleted from the source bucket after\nsuccessful export unless `file.skipDelete` is true (Celigo also keeps its own copy of\nprocessed files for a set period). The connection's AWS credentials must have\ns3:PutObject permission on this bucket."},"keyPrefix":{"type":"string","description":"Prefix prepended to each file's name when it is moved to the backup bucket; accepts\nstatic text or handlebars templates. The original directory structure is not preserved —\nonly the filename is appended to this prefix. Ignored unless backupBucket is set."}}},"Wrapper":{"type":"object","description":"Configuration for Wrapper exports, which delegate data retrieval to custom connector code\n(typically a stack-hosted function) rather than a built-in adaptor. Required when the\n_connectionId field references a wrapper connection.","required":["function"],"properties":{"function":{"type":"string","description":"Name of the function the wrapper invokes to retrieve records. Must match a callable\nfunction in the wrapper's execution context; names are case-sensitive."},"configuration":{"oneOf":[{"type":"object","additionalProperties":true},{"type":"array","maxItems":0}],"description":"Free-form settings passed to the wrapper function at runtime (connector-specific keys such\nas method, apiVersion, headers, relativePath, or body). Structure is defined by the wrapper\ncode, not by this schema. May be stored as an empty array when no settings exist."}}},"Parsers":{"type":"array","description":"Configuration for parsing XML payloads (i.e. files, HTTP responses, etc.). Use this field when you need to process XML data\nand transform it into a JSON records.\n\n**Implementation notes**\n\n- This is where you configure how to parse XML data in your resource\n- Although defined as an array, you typically only need a single parser configuration\n- Currently only XML parsing is supported\n- Only configure this field when working with XML data that needs structured parsing\n","items":{"type":"object","properties":{"version":{"type":"string","description":"Version identifier for the parser configuration format. Currently only version \"1\" is supported.\n\nAlways set this field to \"1\" as it's the only supported version at this time.\n","enum":["1"]},"type":{"type":"string","description":"Defines the type of parser to use. Currently only \"xml\" is supported.\n\nWhile the system is designed to potentially support multiple parser types in the future,\nat this time only XML parsing is implemented, so this field must be set to \"xml\".\n","enum":["xml"]},"name":{"type":"string","description":"Optional identifier for the parser configuration. This field is primarily for documentation\npurposes and is not functionally used by the system.\n\nThis field can be omitted in most cases as it's not required for parser functionality.\n"},"rules":{"type":"object","description":"Configuration rules that determine how XML data is parsed and converted to JSON.\nThese settings control the structure and format of the resulting JSON records.\n\n**Parsing options**\n\nThere are two main parsing strategies available:\n- **Automatic parsing**: Simple but produces more complex output\n- **Custom parsing**: More control over the resulting JSON structure\n","properties":{"V0_json":{"type":"boolean","description":"Controls the XML parsing strategy.\n\n- When set to **true** (Automatic): XML data is automatically converted to JSON without\n  additional configuration. This is simpler to set up but typically produces more complex\n  and deeply nested JSON that may be harder to work with.\n\n- When set to **false** (Custom): Gives you more control over how the XML is converted to JSON.\n  This requires additional configuration (like listNodes) but produces cleaner, more\n  predictable JSON output.\n\nMost implementations use the Custom approach (false) for better control over the output format.\n"},"listNodes":{"type":"array","description":"Paths of elements that always parse as arrays. An element that can repeat parses as an\nobject when it occurs once and as an array when it occurs more than once, so list every\nrepeating element here to give each record the same shape regardless of item count.\nEach entry is the element's full path from the record element selected by the resource\npath (from the document root when there is no resource path), with an optional leading\nslash. Only relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"includeNodes":{"type":"array","description":"Limits which XML nodes are included in the output JSON.\n\nFor large XML documents, you can use this field to extract only the nodes you need,\nreducing the size and complexity of the resulting JSON. Only nodes specified here\n(and their children) will be included in the output.\n\nEach entry should be a simplified XPath expression pointing to nodes to include.\n\nOnly relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"excludeNodes":{"type":"array","description":"Specifies which XML nodes should be excluded from the output JSON.\n\nSometimes it's easier to specify which nodes to exclude rather than which to include.\nUse this field to identify nodes that should be omitted from the output JSON.\n\nEach entry should be a simplified XPath expression pointing to nodes to exclude.\n\nOnly relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"stripNewLineChars":{"type":"boolean","description":"Controls whether newline characters are removed from text values.\n\nWhen set to true, all newline characters (\\n, \\r, etc.) will be removed from\ntext content in the XML before conversion to JSON.\n","default":false},"trimSpaces":{"type":"boolean","description":"Controls whether leading and trailing whitespace is trimmed from text values.\n\nWhen set to true, all values will have leading and trailing whitespace removed\nbefore conversion to JSON.\n","default":false},"attributePrefix":{"type":"string","description":"Specifies a character sequence to prepend to XML attribute names when converted to JSON properties.\n\nIn XML, both elements and attributes can exist at the same level, but in JSON this distinction is lost.\nTo maintain the distinction between element data and attribute data in the resulting JSON, this prefix\nis added to attribute names during conversion.\n\nFor example, with attributePrefix set to \"Att-\" and an XML element like:\n```xml\n<product id=\"123\">Laptop</product>\n```\n\nThe resulting JSON would be:\n```json\n{\n  \"product\": \"Laptop\",\n  \"Att-id\": \"123\"\n}\n```\n\nThis helps maintain the distinction between element content and attribute values in the\nconverted JSON, making it easier to reference specific data in downstream processing steps.\n"},"textNodeName":{"type":"string","description":"Specifies the property name to use for element text content when an element has both\ntext content and child elements or attributes.\n\nWhen an XML element contains both text content and other nested elements or attributes,\nthis field determines what property name will hold the text content in the resulting JSON.\n\nFor example, with textNodeName set to \"value\" and an XML element like:\n```xml\n<item id=\"123\">\n  Laptop\n  <category>Electronics</category>\n</item>\n```\n\nThe resulting JSON would be:\n```json\n{\n  \"item\": {\n    \"value\": \"Laptop\",\n    \"category\": \"Electronics\",\n    \"id\": \"123\"\n  }\n}\n```\n\nThis allows for unambiguous parsing of complex XML structures that mix text content with\nchild elements. Choose a name that's unlikely to conflict with actual element names in your XML.\n"}}}}}},"Mappings":{"type":"array","description":"Array of field mapping configurations for transforming data from one format into another.\n\n**Guidance**\n\nThis schema is designed around RECURSION as its core architectural principle. Understanding this recursive\nnature is essential for building effective mappings:\n\n1. The schema is self-referential by design - a mapping can contain nested mappings of the same structure\n2. Complex data structures (nested objects and arrays of objects, to any depth) are ALL\n   handled through this recursive pattern; an array of arrays is not (build one in a script hook)\n3. Each mapping handles one level of the data structure; deeper levels are handled by nested mappings\n\nWhen generating mappings programmatically:\n- For simple fields (string, number, boolean): Create single mapping objects\n- For objects: Create a parent mapping with nested 'mappings' array containing child field mappings\n- For arrays: Use 'buildArrayHelper' with extract paths defining array inputs and\n  recursive 'mappings' to define object structures\n\nThe system will process these nested structures recursively during runtime, ensuring proper construction\nof complex hierarchical data while maintaining excellent performance.\n\nOn save, a leaf row with neither a source (`extract`, `hardCodedValue`, `lookupName`) nor a\ndestination (`generate`) is dropped silently, at every nesting level, and so is an object or\nobject-array row left with no source, no destination and no remaining child `mappings` or\n`buildArrayHelper` entries; a row with only one of the two, and a source-less, destination-less\nwrapper that still has children, are kept.\n\nThe shape of the array is the mapper's **JSON structure**. Named top-level items produce an object\nrecord (`record { }`). Exactly one top-level item with `dataType: objectarray` and no `generate` is\nthe `rows [ ]` structure: the mapped record is an array — usually one element per element of the\nsource array that root's `buildArrayHelper` iterates — Handlebars templates such as `http.body`\nbind it as `rows` instead of `record`, and the mapping editor shows that row's destination disabled.\n","items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}}},"items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}},"Transform":{"type":"object","description":"Configuration for transforming data during processing operations. This object enables\nreshaping of records.\n\n**Transformation capabilities**\n\nCeligo's transformation engine offers powerful features for data manipulation:\n- Precise field mapping with JSONPath expressions\n- Support for any level of nested arrays\n- Formula-based field value generation\n- Dynamic references to flow and integration settings\n\n**Implementation approaches**\n\nThere are two distinct transformation mechanisms available:\n\n**Rule-Based Transformation (`type: \"expression\"`)**\n- **Best For**: Most transformation scenarios from simple to complex\n- **Capabilities**: Field mapping, formula calculations, lookups, nested data handling\n- **Advantages**: Visual configuration, no coding required, intuitive interface\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear mapping requirements or need to reshape data structure\n\n**Script-Based Transformation (`type: \"script\"`)**\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Capabilities**: Full programmatic control, custom processing, complex business rules\n- **Advantages**: Maximum flexibility, can implement any transformation logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Visual transformation tools aren't sufficient for your use case\n","properties":{"type":{"type":"string","description":"Determines which transformation mechanism to use. This choice affects which properties\nmust be configured and how transformation logic is implemented.\n\n**Available types**\n\n**Rule-Based Transformation (`\"expression\"`)**\n- **Required Config**: The `expression` object with mapping definitions\n- **Behavior**: Applies declarative rules to reshape data\n- **Best For**: Most transformation scenarios from simple to complex\n- **Advantages**: Visual configuration, no coding required\n\n**Script-Based Transformation (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to transform data\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard data transformations, use `\"expression\"`\n2. For complex logic or specialized processing, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based transformations. This object enables reshaping data\nwithout requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define transformation rules that\ncan map, modify, and generate data elements.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Field mappings define how input data is transformed to target fields\n- Formulas can be used to calculate or generate new values\n- Lookups can enrich data by fetching related information\n- Mode determines how records are processed (create new or modify existing)\n","properties":{"version":{"type":"string","description":"Version of the expression format. Determines which rules\nproperty contains the transformation logic.\n","enum":["1","2"]},"rules":{"type":"array","description":"Transformation rules for version 1 expressions. An array of\nrule groups; each group is an array of field-mapping objects.\nMost transforms have a single group. Present when `version`\nis `\"1\"`. The output record contains ONLY the generated\nfields — every unmapped field is dropped (v1 has no\nequivalent of Transform 2.0's `modify` mode), and the\nrecord's trace key does not survive the rebuild.\n","items":{"type":"array","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path to read from. Supports multiple\nsyntaxes: bare field names (`id`), dot notation\n(`fulfillment.shipment_id`), slash-prefixed paths\nfor XML (`/FeedProcessingStatus`), wildcards (`*.id`,\n`*.[Internal ID]`), and array indexing (`SDF[0]`).\n"},"generate":{"type":"string","description":"Target field name to write to. Typically a bare name\n(`id`) or dot path (`SDF.Filter.ID`).\n"},"key":{"type":"string","description":"Auto-generated identifier for this rule, used by the\nUI to track individual rules for editing and reordering.\n"}},"required":["extract","generate"]}}},"rulesTwoDotZero":{"type":"object","description":"Configuration for version 2 transformation rules. This object contains the core logic\nfor how data is mapped, enriched, and transformed.\n\n**Capabilities**\n\nTransformation 2.0 provides:\n- Precise field mapping with JSONPath expressions\n- Support for deeply nested data structures\n- Formula-based field generation\n- Dynamic lookups for data enrichment\n- Multiple operating modes to fit different scenarios\n","properties":{"mode":{"type":"string","description":"Transformation mode that determines how records are handled during processing.\n\n**Available modes**\n\n**Create Mode (`\"create\"`)**\n- **Behavior**: Builds entirely new output records from inputs\n- **Use When**: Output structure differs significantly from input\n- **Advantage**: Clean slate approach, no field inheritance\n\n**Modify Mode (`\"modify\"`)**\n- **Behavior**: Makes targeted edits to existing records\n- **Use When**: Output structure should remain similar to input\n- **Advantage**: Preserves unmapped fields from the original record\n","enum":["create","modify"]},"mappings":{"$ref":"#/components/schemas/Mappings"},"lookups":{"allOf":[{"description":"Shared lookup tables used across all mappings defined in the transformation rules.\n\n**Purpose**\n\nLookups provide centralized value translation that can be referenced from any mapping\nin your transformation configuration. They enable consistent translation of codes, IDs,\nand values between systems without duplicating translation logic.\n\n**Usage in transformations**\n\nLookups are particularly valuable in transformations for:\n\n- **Data Normalization**: Standardizing values from diverse source systems\n- **Code Translation**: Converting between different coding systems (e.g., status codes)\n- **Field Enrichment**: Adding descriptive values based on ID or code lookups\n- **Cross-Reference Resolution**: Mapping identifiers between integrated systems\n\n**Implementation**\n\nLookups are defined once in this array and referenced by name in mappings:\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"statusMapping\",\n    \"map\": {\n      \"A\": \"Active\",\n      \"I\": \"Inactive\",\n      \"P\": \"Pending\"\n    },\n    \"default\": \"Unknown Status\"\n  }\n]\n```\n\nThen referenced in mappings using the lookupName property:\n\n```json\n{\n  \"generate\": \"status\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.statusCode\",\n  \"lookupName\": \"statusMapping\"\n}\n```\n\nThe system automatically applies the lookup during transformation processing.\n\nFor complete details on lookup properties and behavior, see the Lookups schema.\n"},{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the JSON shape the transformTwoDotZero processor\nevaluates `mappings[].extract` JSONPath values against at\nflow runtime. Applies only to Transform 2.0 (v2,\n`rulesTwoDotZero`); v1 transforms (the `rules` array on\n`transform.expression.rules`) and script-mode transforms\nignore this field.\n"}}}}},"script":{"type":"object","description":"Configuration for programmable script-based transformations. This object enables complex, custom\ntransformation logic beyond what expression-based transformations can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to transform data according to\nspecialized business rules or complex algorithms.\n\n**Implementation approach**\n\nScript-based transformation works by:\n1. Executing the specified function from the referenced script\n2. Passing input data to the function\n3. Using the function's return value as the transformed output\n\n**Common use cases**\n\nScript transformation is ideal for:\n- Complex business logic that can't be expressed through mappings\n- Algorithmic transformations requiring computation\n- Dynamic transformations based on external factors\n- Legacy system data format compatibility\n- Multi-stage processing with intermediate steps\n\nOnly use script-based transformation when expression-based transformation is insufficient.\nScript transformation requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to a predefined script resource containing the transformation logic.\n\nThe referenced script should contain the function specified in the\n'function' property. It must be a script in this account: any other id fails the save\nwith 422 (\"Only a script owned by the user or a script shared by the Integration App can\nbe used at transform.script._scriptId.\").\n","format":"objectid"},"function":{"type":"string","description":"Name of the function within the script to execute for transformation. This function\nmust exist in the script referenced by _scriptId.\n"}}}}},"Lookups":{"type":"array","description":"Configuration for value-to-value transformations using lookup tables.\n\n**Purpose**\n\nLookups provide a way to translate values from one system to another. They transform\ninput values into output values using either static mapping tables or\ndynamic lookup caches.\n\n**Lookup mechanisms**\n\nThere are two distinct lookup mechanisms available:\n\n1. **Static Lookups**: Define a simple key-value map object and store it as part of your resource\n   - Best for: Small, fixed sets of values that rarely change\n   - Implementation: Configure the `map` object with input-to-output value mappings\n   - Example: Country codes, status values, simple translations\n\n2. **Dynamic Lookups**: Reference an existing 'Lookup Cache' resource in your Celigo account\n   - Best for: Large datasets, frequently changing values, or complex reference data\n   - Implementation: Configure `_lookupCacheId` to reference cached data maintained independently\n   - Example: Product catalogs, customer databases, pricing information\n\n**Property usage**\n\nThere are two mutually exclusive ways to configure lookups, depending on which mechanism you choose:\n\n1. **For Static Mappings**: Configure the `map` property with a direct key-value object\n   ```json\n   \"map\": {\"US\": \"United States\", \"CA\": \"Canada\"}\n   ```\n\n2. **For Dynamic Lookups**: Configure the following properties:\n   - `_lookupCacheId`: Reference to the lookup cache resource\n   - `extract`: JSON path to extract specific value from the returned lookup object\n\n**When to use**\n\nLookups are ideal for:\n\n1. **Value Translation**: Mapping codes or IDs to human-readable values\n\n2. **Data Enrichment**: Adding related information to records during processing\n\n3. **Normalization**: Ensuring consistent formatting of values across systems\n\n**Implementation details**\n\nLookups can be referenced in:\n\n1. **Field Mappings**: Direct use in field transformation configurations\n\n2. **Handlebars Templates**: Use within templates with the syntax:\n   ```\n   {{lookup 'lookupName' record.fieldName}}\n   ```\n\n**Example usage**\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"countryCodeToName\",\n    \"map\": {\n      \"US\": \"United States\",\n      \"CA\": \"Canada\",\n      \"UK\": \"United Kingdom\"\n    },\n    \"default\": \"Unknown Country\",\n    \"allowFailures\": true\n  },\n  {\n    \"name\": \"productDetails\",\n    \"_lookupCacheId\": \"60a2c4e6f321d800129a1a3c\",\n    \"extract\": \"$.details.price\",\n    \"allowFailures\": false\n  }\n]\n```\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for the lookup table within this configuration.\n\nThis name must be unique within the scope where the lookup is defined and is used to reference\nthe lookup in handlebars templates with the syntax {{lookup 'name' value}}.\n\nChoose descriptive names that indicate the transformation purpose, such as:\n- \"countryCodeToName\" for country code to full name conversion\n- \"statusMapping\" for status code translations\n- \"departmentCodes\" for department code to name mapping\n"},"map":{"type":["object","null"],"description":"The lookup mapping table as key-value pairs. The platform stores `null`\nhere on dynamic lookups, which resolve values at runtime instead of\nfrom a static table.\n\nThis object contains the input values as keys and their corresponding\noutput values. When a input value matches a key in this object,\nit will be replaced with the corresponding value.\n\nThe map should be kept to a reasonable size (typically under 100 entries)\nfor optimal performance. For larger mapping requirements, consider using\ndynamic lookups instead.\n\nMaps can include:\n- Simple code to name conversions: {\"US\": \"United States\"}\n- Status transformations: {\"A\": \"Active\", \"I\": \"Inactive\"}\n- ID to name mappings: {\"100\": \"Marketing\", \"200\": \"Sales\"}\n\nValues can be strings, numbers, or booleans, but all are stored as strings\nin the configuration.\n"},"_lookupCacheId":{"type":"string","description":"Reference to a LookupCache resource that contains the reference data for the lookup.\n\n**Purpose**\n\nThis field connects the lookup to an external data source that has been cached in the system.\nUnlike static lookups that use the `map` property, dynamic lookups can reference large datasets\nor frequently changing information without requiring constant updates to the integration.\n\n**Implementation details**\n\nThe LookupCache resource referenced by this ID contains:\n- The data records to be used as a reference source\n- Configuration for how the data should be indexed and accessed\n- Caching parameters to balance performance with data freshness\n\n**Usage patterns**\n\nCommonly used to reference:\n- Product catalogs or SKU databases\n- Customer or account information\n- Pricing tables or discount rules\n- Complex business logic lookup tables\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n","format":"objectid"},"extract":{"type":"string","description":"JSON path expression that extracts a specific value from the cached lookup object.\n\n**Purpose**\n\nWhen using dynamic lookups with a LookupCache, this JSON path identifies which field to extract\nfrom the cached object after it has been retrieved using the lookup key.\n\n**Implementation details**\n\n- Must use JSON path syntax (similar to mapping extract fields)\n- Operates on the cached object returned by the lookup operation\n- Examples:\n  - \"$.name\" - Extract the name field from the top level\n  - \"$.details.price\" - Extract a nested price field\n  - \"$.attributes[0].value\" - Extract a value from the first element of an array\n\n**Usage scenario**\n\nWhen a lookup cache contains complex objects:\n```json\n// Cache entry for key \"PROD-123\":\n{\n  \"id\": \"PROD-123\",\n  \"name\": \"Premium Widget\",\n  \"details\": {\n    \"price\": 99.99,\n    \"currency\": \"USD\",\n    \"inStock\": true\n  }\n}\n```\n\nSetting extract to \"$.details.price\" would return 99.99 as the lookup result.\n\nIf no extract is provided, the entire cached object is returned as the lookup result.\n"},"default":{"type":["string","null"],"description":"Default value to use when the source value is not found in the lookup map.\nThe platform stores `null` here when no default is configured.\n\nThis value is used as a fallback when:\n1. The source value doesn't match any key in the map\n2. allowFailures is true or omitted\n\nSetting an appropriate default helps prevent flow failures due to unexpected\nvalues and provides predictable behavior for edge cases.\n\nCommon default patterns include:\n- Descriptive unknowns: \"Unknown Country\", \"Unspecified Status\"\n- Original value indicators: \"{Original Value}\", \"No mapping found\"\n- Neutral values: \"Other\", \"N/A\", \"Miscellaneous\"\n\nA save refuses a default together with an explicit `allowFailures: false`\n(422 `Lookups with default set can not have allowFailures explicitly set to\nfalse.`): a lookup either fails a miss (`allowFailures: false`, no default) or\nfalls back to its default.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, missing lookup values will use the default value rather than causing an error.\n\n**Behavior control**\n\nThis field determines how the system handles source values that don't exist in the map:\n\n- true: Use the default value for missing mappings and continue processing\n- false: Treat missing mappings as errors, failing the record (`lookup_error` for a\n  static map, `lookupcache_evaluation_failed` for a lookup cache)\n- omitted or null: the record doesn't fail either. On an import a static lookup writes\n  its default if it has one, and a static or cache lookup without one leaves the\n  mapped field out of the output, with no error\n\n**Recommendation**\n\nSet this to true when:\n- New source values might appear over time\n- Data quality issues could introduce unexpected values\n- Processing should continue even with imperfect mapping\n\nSet this to false when:\n- Complete data accuracy is critical\n- All possible source values are known and controlled\n- Missing mappings indicate serious data problems that should be addressed\n\nA default raises no error: records that miss go out with the default value and\nnothing reports them. Use true only when that fallback is acceptable at the\ndestination, and set false explicitly when a missing value must stop the record.\n"}}}},"Form":{"type":"object","description":"Configuration for creating user-friendly settings forms that make it easier for less technical users\nto configure integration resources.\n\n**Settings form builder**\n\nThe Settings Form Builder allows you to create or edit user-friendly fields that prompt for text entry\nor selections that will be returned as settings applied to this resource. Your forms can include any\nfield types that you see elsewhere in integrator.io, such as:\n\n- Text fields\n- Dropdown selections\n- Checkboxes\n- Radio buttons\n- Date pickers\n- Multi-select fields\n- Search fields\n\nForm fields make it much easier for less technical users to work with your integration settings by:\n\n- Providing clear labels and help text\n- Enforcing validation rules\n- Offering pre-defined selection options\n- Grouping related settings logically\n- Supporting conditional visibility\n- Creating a consistent user experience\n","properties":{"form":{"type":"object","description":"Configuration that defines the structure, fields, and behavior of the settings form.\n\nThis object contains the complete definition of the form's layout, fields, validation rules,\nand interactive behaviors. The specific structure depends on the form complexity and can include\nfield definitions, sections, conditional display logic, and default values.\n\nThe form configuration is typically created and managed through the visual Form Builder interface\nrather than edited directly as JSON.\n","properties":{"fieldMap":{"type":"object","description":"A mapping of field identifiers to their configuration objects.\nEach key in this object represents a unique field ID, and the value contains\nall the configuration settings for that specific form field.\n","additionalProperties":{"type":"object","description":"Configuration for an individual form field.\n","properties":{"id":{"type":"string","description":"Unique identifier for this field within the form: layout containers and conditions\nrefer to the field by it. Use the same string as the field's fieldMap key and its `name`.\n"},"name":{"type":"string","description":"Key the field's value is saved under: one top-level key of the resource's `settings`,\nwhich the form also reads the value back from when it opens. Nested objects inside\n`settings` are written to it directly, not through a form field's `name`.\n"},"type":{"type":"string","description":"The type of form control to render for this field.\n","enum":["text","checkbox","radiogroup","relativeuri","editor","keyvalue","select","multiselect","toggle","datetime","date","exportSelect","staticMap"]},"label":{"type":"string","description":"Display label shown next to the field in the form.\n"},"description":{"type":"string","description":"Detailed explanation text that appears below the field, providing more context\nthan the label or helpText.\n"},"helpText":{"type":"string","description":"Explanatory text that appears when hovering over the help icon next to the field.\nUsed to provide additional guidance on how to use the field.\n"},"required":{"type":"boolean","description":"When true, the field must have a value before the form can be submitted. Only the form\nenforces it: an API write of `settings` is stored without the value.\n","default":false},"defaultValue":{"description":"Value the form shows while the resource's `settings` holds no value under the field's\n`name`.\n"},"multiline":{"type":"boolean","description":"For text fields, determines whether the input should be a multi-line text area\ninstead of a single-line input.\n","default":false},"rowsMax":{"type":"integer","description":"For multiline text fields, specifies the maximum number of visible rows.\n"},"inputType":{"type":"string","description":"For text fields, specifies the HTML input type attribute to apply additional\nvalidation or specialized input behavior.\n","enum":["text","number","email","password","tel","url"]},"delimiter":{"type":"string","description":"For text fields, specifies a character to use for splitting the input into an array.\nUsed for collecting multiple values in a single text field.\n"},"mode":{"type":"string","description":"For editor fields, specifies the type of content being edited for syntax highlighting.\n","enum":["json","xml","csv","text"]},"keyName":{"type":"string","description":"For keyvalue and staticMap fields, names the first column: each saved row holds that\ncolumn's value under this key. A keyvalue field without it uses `key`.\n"},"valueName":{"type":"string","description":"For keyvalue and staticMap fields, names the second column: each saved row holds that\ncolumn's value under this key. A keyvalue field without it uses `value`.\n"},"keyLabel":{"type":"string","description":"For staticMap fields, heading of the first column.\n"},"valueLabel":{"type":"string","description":"For staticMap fields, heading of the second column.\n"},"keyOptions":{"type":"array","description":"For staticMap fields, suggestions offered in the first column's cells. A cell still\naccepts a typed value.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"Display text for the suggestion.\n"},"value":{"type":"string","description":"Value saved when the suggestion is chosen.\n"}}}},"valueOptions":{"type":"array","description":"For staticMap fields, suggestions offered in the second column's cells. A cell still\naccepts a typed value.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"Display text for the suggestion.\n"},"value":{"type":"string","description":"Value saved when the suggestion is chosen.\n"}}}},"keyResource":{"type":"object","description":"For staticMap fields, fills the first column's suggestions from an export the form\ncarries inline in `virtual`, run the way an exportSelect's `resource` is. With\n`keyResource` or `valueResource` set, the form requires both columns in every row\nunless `keyValueRequired` is false.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline, shaped like an exportSelect's `resource.virtual`.\n","additionalProperties":true}}},"valueResource":{"type":"object","description":"For staticMap fields, fills the second column's suggestions from an export the form\ncarries inline in `virtual`, run the way an exportSelect's `resource` is.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline, shaped like an exportSelect's `resource.virtual`.\n","additionalProperties":true}}},"keyValueRequired":{"type":"boolean","description":"For staticMap fields with `keyResource` or `valueResource`, when false, saves rows\nthat fill only one column; by default the form drops them.\n","default":true},"resource":{"type":"object","description":"For exportSelect fields, the export whose records become the options. When the form\nopens, the UI previews the export in `virtual` (with `test.limit` 100 unless the export\nsets its own) and lists the preview's `data`: each record needs a `label` and a `value`\n(a string or number serves as both), so shape them with the export's `transform`. A\nsaved export can't be referenced, and an HTTP export that finds no records leaves the\nfield showing an error instead of options.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline: `adaptorType`, its adaptor block, and an optional\n`transform`. `_connectionId` defaults to the connection of the export or import the\nform belongs to; set it on any other resource's form.\n","additionalProperties":true}}},"multiselect":{"type":"boolean","description":"For exportSelect fields, when true, the user can choose several options and the value\nis saved as an array.\n","default":false},"supportHandlebars":{"type":"boolean","description":"For exportSelect fields, when true, renders the `resource.virtual` export as a Handlebars\ntemplate before each preview (all but its `transform`, `hooks`, and `filter`). On an\nintegration, flow group, flow, export, or import form, the form's current values are\nunder that form's scope name (`integration`, `flowGrouping`, `flow`, `export`, or\n`import`), beside the saved settings of the scopes above it, with no `settings.`\nprefix; on a connection or iClient form the template has no context. The options then\nload through `refreshOptionsOnChangesTo` or `typeAheadSearch`, or when the user\nrefreshes the field.\n","default":false},"refreshOptionsOnChangesTo":{"type":"array","items":{"type":"string"},"description":"For exportSelect fields with `supportHandlebars`, ids of the fields whose values the\ntemplated export uses: the options load once one of them has a value and reload whenever\none changes.\n"},"typeAheadSearch":{"type":"boolean","description":"For exportSelect fields, when true, re-runs the templated export as the user types, with\nthe typed text as this field's own value; an empty box clears the options, and typed\ntext is accepted as the value.\n","default":false},"showDelete":{"type":"boolean","description":"For keyvalue fields, determines whether to show a delete button for each key-value pair.\n"},"doNotAllowFutureDates":{"type":"boolean","description":"For date and datetime fields, restricts selection to dates not in the future.\n"},"skipTimezoneConversion":{"type":"boolean","description":"For datetime fields, prevents automatic timezone conversion of the date value.\n"},"options":{"type":"array","description":"For fields that present choices (select, multiselect, radiogroup, toggle), defines\nthe available options.\n","items":{"anyOf":[{"title":"Option group","type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"oneOf":[{"title":"String value","type":"string"},{"title":"Label-value pair","type":"object","properties":{"label":{"type":"string","description":"Display text for the option.\n"},"value":{"type":"string","description":"Value to store when this option is selected.\n"}}}]},"description":"Array of option values/labels to display in the selection control.\n"}}},{"title":"Label-value pair","type":"object","required":["label"],"properties":{"label":{"type":"string","description":"Display text for the option.\n"},"value":{"type":"string","description":"Value to store when this option is selected.\n"}}}]}},"visibleWhen":{"type":"array","description":"Conditional display rules that determine when this field should be visible.\nIf empty or not provided, the field is always visible.\n","items":{"type":"object","properties":{"field":{"type":"string","description":"The ID of another field whose value controls the visibility of this field. The\nsave doesn't check it: a rule naming an ID that isn't in the form counts as met,\nso the field stays visible.\n"},"is":{"type":"array","items":{"type":["string","boolean","number","null"]},"description":"Array of values - if the referenced field has any of these values,\nthis field will be visible. Values may be strings, booleans (for\ncheckbox/toggle fields), numbers (for numeric inputs), or null.\n"}}}}}}},"layout":{"type":"object","description":"Defines how the form fields are arranged and grouped in the UI.\nThe layout can organize fields into columns, sections, or other visual groupings.\n","properties":{"type":{"type":"string","description":"The type of layout to use for the form.\n","enum":["column","collapse","box","indent","tabWithoutSave","verticalTabWithoutSave"]},"fields":{"type":"array","items":{"type":"string"},"description":"Field IDs shown above the containers, without a heading.\n"},"containers":{"type":"array","description":"Array of container objects that group fields or contain nested containers.\nEach container can represent a column, box, indented section, or collapsible section.\n","items":{"type":"object","properties":{"type":{"type":"string","description":"The visual style of the container.\n","enum":["indent","box","collapse"]},"label":{"type":"string","description":"The heading text displayed for this container.\n"},"fields":{"type":"array","items":{"type":"string"},"description":"Array of field IDs that should be displayed in this container.\nEach ID must correspond to a key in the fieldMap object: the save doesn't check it,\nand an ID that doesn't renders nothing. A field the layout doesn't list isn't shown.\n"},"containers":{"type":"array","description":"Nested containers within this container. Allows for hierarchical organization\nof fields with different visual styles.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"The heading text displayed for this nested container.\n"},"fields":{"type":"array","items":{"type":"string"},"description":"Array of field IDs that should be displayed in this nested container.\n"}}}}}}}}}},"additionalProperties":true},"init":{"type":"object","description":"Configuration for custom JavaScript initialization that executes when the form is first loaded.\n\nThis object defines a JavaScript hook that prepares the form for use, sets initial field values,\nperforms validation, or otherwise customizes the form behavior before it is displayed to the user.\n\n**Function signature**\n\nThe initialization function is invoked with a single 'options' argument containing contextual information:\n```javascript\nfunction formInit(options) {\n  // Process options and return the form object\n  return options.resource.settingsForm.form;\n}\n```\n\n**Available context**\n\nThe 'options' argument provides access to:\n- `options.resource` - The current resource being configured\n- `options.parentResource` - The parent of the current resource\n- `options.grandparentResource` - The grandparent of the current resource\n- `options.license` - For integration apps, the license provisioned to the integration\n- `options.parentLicense` - For integration apps, the parent of the license\n- `options.sandbox` - Boolean flag indicating whether the script is running in a sandbox environment\n\n\n**Common uses**\n\n- Dynamically generate field options based on resource configuration\n- Pre-populate default values from related resources\n- Apply conditional logic that depends on resource properties\n- Add, remove, or modify form fields based on user permissions or account settings\n- Fetch external data to populate selection options\n- Implement complex validation rules that depend on resource context\n- Create branching form experiences based on user selections\n\n**Return value**\n\nThe function must return a valid form object that the UI can render.\nThrowing an exception will signal an error to the user.\n\nThe UI runs the function each time it opens the form and renders the returned form instead of\n`form`, then fills in the values from `settings`; a thrown error's message replaces the form.\n","properties":{"function":{"type":"string","description":"The name of the function to execute within the referenced script.\n\nThis property specifies which function to invoke from the script\nreferenced by _scriptId. The function will be called when the form\nis initialized and should handle any custom setup logic.\n\nThe function must follow the expected signature and return a valid form object.\n"},"_scriptId":{"type":"string","format":"objectId","description":"Reference to a predefined script resource containing the initialization function.\n\nThe referenced script should contain the function specified in the\n'function' property. On an integration, a flow group, a flow, an export, an import, or a\nconnection, an id that isn't a script in the same account (or, on an Integration App's\nresources, one the app shares) is refused with 422 `invalid_ref`. An iClient save\nstores the id without that check.\n"}}}}},"Settings":{"type":"object","description":"Configuration settings that can be accessed by hooks, filters, mappings and handlebars templates at runtime.\n\nIt enables customization of the resource's logic, allowing hooks, mappings, filters, and\nhandlebars to access and apply the settings at runtime.\n\n**Usage**\n\nThe settings object can store arbitrary JSON data that you want to save with the resource.\nWhile it's often populated through a form defined in the `settingsForm` field, you can also:\n\n- Directly provide JSON settings without using a form\n- Store configuration values used by hooks and templates\n- Create resource-specific constants and parameters\n- Maintain lookup tables or mapping structures\n- Define conditional logic parameters\n\n**Accessibility**\n\nSettings are available in:\n- All handlebars fields for building dynamic payloads\n- Field mapping expressions\n- JavaScript hooks via the options object\n- Filters and transformations\n\nAt runtime, the settings objects in the step's execution chain are gathered into a\nsingle `settings` context keyed by fixed scope keys. Flow runs populate the full\nchain: `settings.integration.*`, `settings.flowGrouping.*` (the flow group the flow\nbelongs to), `settings.flow.*`, `settings.connection.*`, `settings.iClient.*`, and\nthe running step's own scope — `settings.export.*` on exports/lookups,\n`settings.import.*` on imports (the key matches the step type). Steps executing\ninside a My API or a Tool receive only the step's own scope plus\n`settings.connection.*` / `settings.iClient.*`; the integration, flowGrouping, and\nflow scopes resolve empty there. The scope key is literal — resource display names\nand settingsForm section labels are never part of the path. The segments after the\nscope mirror the stored settings JSON key path exactly: a top-level field is\n`settings.<scope>.<fieldId>`, and nested objects add one segment per JSON key\n(e.g. `settings.flowGrouping.Customer.region` when the group's settings JSON nests\n`region` under `Customer`). References without a scope key\n(e.g. `{{settings.myField}}`) resolve to empty strings.\n\n**Best practices**\n\nFor non-technical users, create a custom form instead of editing the JSON directly.\nThis provides a user-friendly interface for updating settings without requiring JSON knowledge.\n","additionalProperties":true},"MockOutput":{"type":["object","null"],"description":"Sample data that simulates the output from an export for testing and configuration purposes.\n\nMock output allows you to configure and test flows without executing the actual export or\nwaiting for real-time data to arrive. This is particularly useful for:\n- Initial flow configuration and testing\n- Mapping development without requiring live data\n- Generating metadata for downstream flow steps\n- Creating realistic test scenarios\n- Documenting expected data structures\n\n**Structure**\n\nThe mock output must follow the integrator.io canonical format, which consists of a\n`page_of_records` array containing record objects. Each record object has a `record`\nproperty that contains the actual data fields.\n\n```json\n{\n  \"page_of_records\": [\n    {\n      \"record\": {\n        \"field1\": \"value1\",\n        \"field2\": \"value2\",\n        ...\n      }\n    },\n    ...\n  ]\n}\n```\n\n**Usage**\n\nIn a flow test run, and for an upstream lookup during a page-processor preview\n(`previewPageProcessor`), integrator.io substitutes this mock output for the export's live\nresponse instead of calling the source. A saved mock is not read anywhere else: a target\nlookup in that preview runs its live query, invoking the export\n(`POST /v1/exports/{_id}/invoke`) reads the source, and real runs ignore it. This allows you\nto:\n- Test mappings with representative data\n- Configure downstream flow steps without waiting for real data\n- Simulate various data scenarios\n\nWhere it is substituted, the records stand in for the RAW parsed response: the export's own\n`transform` and output `filter` still run on top of them, so downstream steps — and a\nlookup's results mapping — see the transformed, filtered records, not the shape stored here.\n\nIn a flow test run of an HTTP export with a saved mock (live probe, 2026-10-21), the export\nstill listed `request` and `response` stages: `request` held the request the export composes,\nand `response` held the mock under the source's URL, as did the test run's request log, which\nrecorded it as a 200. Read those stages as the substituted mock, not as a live call.\n\n`POST /v1/exports/preview` reads the `mockOutput` in its request body, not the saved one. For\nan export the preview supports (an HTTP export, for example), a body that carries it (as an\nexport read back with GET does) returns that snapshot as a lone `parse` stage without calling\nthe source; remove `mockOutput` from the body to preview live records.\n\n**Limitations**\n\n- Maximum of 10 records\n- Maximum size of 1 MB\n- Must follow the canonical format shown above\n\nMock output can be populated automatically from preview data (which is the parsed response,\nbefore `transform` / `filter`) or entered manually.\n","properties":{"page_of_records":{"type":"array","description":"Array of record objects in the integrator.io canonical format.\n\nEach item in this array represents one record that would be processed\nby the flow during execution.\n","items":{"type":"object","properties":{"record":{"type":"object","description":"Container for the actual record data fields.\n\nThe structure of this object will vary depending on the specific\nexport configuration and the source system's data structure.\n","additionalProperties":true}}}}}},"PreSave":{"type":"object","description":"Defines a JavaScript hook that executes before the resource is saved.\n\nThis hook allows for programmatic validation, transformation, or enrichment of the\nresource itself before it is persisted. It can be used to enforce business rules,\nset derived properties, or implement cross-field validations that can't be expressed\nthrough the standard UI.\n\n**Function signature**\n\nThe preSave function is invoked with a single 'options' argument containing:\n```javascript\nfunction preSave(options) {\n  // Process options and return the modified resource\n  return options.newResource;\n}\n```\n\n**Available context**\n\nThe 'options' argument provides access to:\n- `options.newResource` - The resource being saved (with pending changes)\n- `options.oldResource` - The previous version of the resource (before changes)\n- `options.sandbox` - Boolean flag indicating whether the script is running in a sandbox environment\n\n\n**Common uses**\n\n- Enforcing complex business rules across multiple fields\n- Automatically deriving field values based on other configuration\n- Performing validation that depends on external systems or data\n- Normalizing or standardizing configuration values\n- Adding computed or derived properties\n- Implementing versioning or change tracking\n- Dynamically looking up data using the Celigo API module to enrich configuration\n\n**Return value**\n\nThe function must return the newResource object (potentially modified) to be saved.\nThrowing an exception will prevent saving and signal an error to the user.\n","properties":{"function":{"type":"string","description":"The name of the function to execute within the referenced script.\n\nThis property specifies which function to invoke from the script\nreferenced by _scriptId. The function will be called just before\nthe resource is saved.\n\nThe function must follow the expected signature and return the resource object.\n"},"_scriptId":{"type":"string","format":"objectId","description":"Reference to a predefined script resource containing the preSave function.\n\nThe referenced script should contain the function specified in the\n'function' property. This script must be accessible within the user's account\nand have appropriate permissions.\n"}}},"ResourceResponse":{"type":"object","description":"Response","properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Unique identifier for the resource. Format is a 24-character hexadecimal string."},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was created. Set automatically and cannot be modified."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was last updated. Changes whenever any property is modified."},"deletedAt":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp when the resource was soft-deleted. When null or absent, the resource is active."}},"required":["_id"]},"IAResourceResponse":{"type":"object","description":"Integration app response fields for resources that are part of integration apps","properties":{"_integrationId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the specific integration instance that contains this resource.\n\nThis field is only populated for resources that are part of an integration app\ninstallation. It contains the unique identifier (_id) of the integration\nresource that was installed in the account.\n\nThe integration instance represents a specific installed instance of an\nintegration app, with its own configuration, settings, and runtime environment.\n\nThis reference enables:\n- Tracing the resource back to its parent integration instance\n- Permission and access control based on integration ownership\n- Lifecycle management (enabling/disabling, updating, or uninstalling)\n"},"_connectorId":{"type":"string","format":"objectId","readOnly":true,"description":"Integration App (connector) that defines this resource. Present only on resources an\nIntegration App owns. The app locks parts of these documents, and a PUT that changes a\nlocked field answers 200 but keeps the stored value, so read the document back to confirm\na change."}}},"AIDescription":{"type":"object","description":"AI-generated descriptions and documentation for the resource.\n\nThis object contains automatically generated content that helps users\nunderstand the purpose, behavior, and configuration of the resource without\nrequiring them to analyze the technical details. The AI-generated content\nis sanitized and safe for display in the UI.\n","properties":{"summary":{"type":["string","null"],"description":"Brief AI-generated summary of the resource's purpose and functionality.\n\nThis concise description provides a quick overview of what the resource does,\nwhat systems it interacts with, and its primary role in the integration.\nThe summary is suitable for display in list views, dashboards, and other\ncontexts where space is limited.\n\nMaximum length: 10KB\n"},"detailed":{"type":["string","null"],"description":"Comprehensive AI-generated description of the resource's functionality.\n\nThis detailed explanation covers the resource's purpose, configuration details,\ndata flow patterns, filtering logic, and other technical aspects. It provides\nin-depth information suitable for documentation, tooltips, or detailed views\nin the administration interface.\n\nThe content may include HTML formatting for improved readability.\n\nMaximum length: 10KB\n"},"generatedOn":{"type":["string","null"],"format":"date-time","description":"Timestamp indicating when the AI description was generated.\n\nThis field helps track the freshness of the AI-generated content and\ndetermine when it might need to be regenerated due to changes in the\nresource's configuration or behavior.\n\nThe timestamp is recorded in ISO 8601 format with UTC timezone (Z suffix).\n"}}},"APIM":{"type":"array","description":"Read-only field that stores information about the integration resources\npublished in the API Management (APIM) platform.\n\nThis field tracks the relationship between integrator.io resources and their\npublished counterparts in the APIM platform, which is\ntightly integrated with the Celigo UI. When resources are \"pushed\" to APIM,\nthis field is populated with the relevant identifiers and statuses.\n","items":{"type":"object","properties":{"apiId":{"type":"string","description":"Identifier for the API where this integrator.io resource is published in the APIM.\n\nThis is an APIM resource identifier (not prefixed with underscore like Celigo IDs)\nthat uniquely identifies the API in the API Management platform.\n"},"flowId":{"type":"string","description":"Identifier for the flow within the API where this integrator.io resource is linked.\n\nWhen an API has multiple integrator.io resources linked, each resource is associated\nwith a specific flow in the API, identified by this field. This is an APIM\nresource identifier.\n"},"status":{"type":"string","description":"Indicates the publishing stage of the integrator.io resource in APIM.\n\nPossible values:\n- 'oaspending': The resource is published but the OpenAPI Specification (OAS) is not\n  yet published. The apiId will be updated with the API ID created in APIM.\n- 'published': The OpenAPI Specification for the integrator.io resource has been\n  successfully uploaded to APIM.\n","enum":["oaspending","published"]}}}}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}}}},"paths":{"/v1/exports":{"get":{"summary":"List exports","description":"Returns a list of all exports configured in the account.\nIf no exports exist in the account, a 204 response with no body will be returned.\n","operationId":"listExports","tags":["Exports"],"parameters":[{"name":"externalId","in":"query","description":"Filter to exports matching this exact external identifier.","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Maximum number of exports to return per page.","schema":{"type":"integer","minimum":1}},{"$ref":"#/components/parameters/After"},{"$ref":"#/components/parameters/Include"},{"$ref":"#/components/parameters/Exclude"}],"responses":{"200":{"description":"Successfully retrieved list of exports","headers":{"Link":{"description":"RFC-5988 pagination links. When more pages remain, includes a `<...>; rel=\"next\"` entry;\nabsent on the final page.\n","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Export"}}}}},"204":{"description":"No exports exist in the account"},"401":{"$ref":"#/components/responses/401-unauthorized"}}}}}}
````

## Create an export

> Creates a new export configuration that can be used to retrieve data from applications\
> or external sources.<br>

````json
{"openapi":"3.2.0","info":{"title":"Exports","version":"1.0.0"},"tags":[{"name":"Exports","description":"Exports retrieve data from source systems — on a schedule, in delta mode, in real time,\non demand, or as a file/blob transfer — package the results into ≤ 5-MB pages, and pass\neach page to downstream flow steps. Depending on configuration, an export surfaces in the\nFlow Builder as an export, a real-time listener, a file transfer, or a mid-flow lookup.\n\n## Export schema\n\n{% openapi-schemas spec=\"export\" schemas=\"Export\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"Request":{"type":"object","description":"Fields that can be sent when creating or updating an export. Set the adaptor-specific\nconfiguration object matching `adaptorType`, and the mode-specific object matching `type`.\n`SimpleExport` (data-loader) is the exception — it needs no adaptor config object. An Event\nStreams listener (`TopicExport`) carries `topic` and `type: stream` and no `_connectionId`.","required":["name"],"allOf":[{"$ref":"#/components/schemas/ExportBase"}],"if":{"properties":{"adaptorType":{"const":"HTTPExport"}},"required":["adaptorType"]},"then":{"required":["http"],"if":{"properties":{"assistant":{"const":"azuredatalakestoragegen2"}},"required":["assistant"]},"then":{"required":["file"],"properties":{"file":{"required":["directory"],"properties":{"directory":{"required":["name"]}}}}},"else":{"if":{"properties":{"assistant":{"const":"googlecloudstorage"}},"required":["assistant"]},"then":{"required":["file"],"properties":{"file":{"required":["directory"],"properties":{"directory":{"required":["storageRootId","id"]}}}}},"else":{"if":{"required":["http"],"properties":{"http":{"required":["type"],"properties":{"type":{"const":"file"}},"not":{"required":["formType"],"properties":{"formType":{"const":"assistant"}}}}}},"then":{"required":["file"]}}}},"else":{"if":{"properties":{"adaptorType":{"const":"FTPExport"}},"required":["adaptorType"]},"then":{"required":["ftp"]},"else":{"if":{"properties":{"adaptorType":{"const":"AS2Export"}},"required":["adaptorType"]},"then":{"if":{"not":{"properties":{"type":{"const":"webhook"}},"required":["type"]}},"then":{"required":["file"]}},"else":{"if":{"properties":{"adaptorType":{"const":"S3Export"}},"required":["adaptorType"]},"then":{"required":["s3"]},"else":{"if":{"properties":{"adaptorType":{"const":"NetSuiteExport"}},"required":["adaptorType"]},"then":{"required":["netsuite"]},"else":{"if":{"properties":{"adaptorType":{"const":"NetSuiteHTTPExport"}},"required":["adaptorType"]},"then":{"required":["http","nsDomainType"]},"else":{"if":{"properties":{"adaptorType":{"const":"SalesforceExport"}},"required":["adaptorType"]},"then":{"required":["salesforce"]},"else":{"if":{"properties":{"adaptorType":{"const":"JDBCExport"}},"required":["adaptorType"]},"then":{"required":["jdbc"]},"else":{"if":{"properties":{"adaptorType":{"const":"RDBMSExport"}},"required":["adaptorType"]},"then":{"required":["rdbms"]},"else":{"if":{"properties":{"adaptorType":{"const":"MongodbExport"}},"required":["adaptorType"]},"then":{"if":{"properties":{"type":{"const":"stream"}},"required":["type"]},"then":{"required":["cdc"]},"else":{"required":["mongodb"],"properties":{"mongodb":{"required":["method","collection"]}}}},"else":{"if":{"properties":{"adaptorType":{"const":"DynamodbExport"}},"required":["adaptorType"]},"then":{"required":["dynamodb"]},"else":{"if":{"properties":{"adaptorType":{"const":"WrapperExport"}},"required":["adaptorType"]},"then":{"required":["wrapper"]},"else":{"if":{"properties":{"adaptorType":{"const":"WebhookExport"}},"required":["adaptorType"]},"then":{"required":["webhook"]},"else":{"if":{"properties":{"adaptorType":{"const":"FileSystemExport"}},"required":["adaptorType"]},"then":{"required":["filesystem"]},"else":{"if":{"properties":{"adaptorType":{"const":"RESTExport"}},"required":["adaptorType"]},"then":{"required":["http"]},"else":{"if":{"properties":{"adaptorType":{"const":"TopicExport"}},"required":["adaptorType"]},"then":{"required":["topic"],"properties":{"topic":{"required":["_topicId"]}}}}}}}}}}}}}}}}}}},"ExportBase":{"type":"object","description":"Writable export fields shared by the request and response schemas.","properties":{"name":{"type":"string","maxLength":100,"description":"Display name for the export, shown in the flow builder, job history, and error logs.\nDescriptive, unique names indicating the data source and purpose make large accounts easier to manage."},"description":{"type":["string","null"],"description":"Free-text summary of what the export retrieves and why. Shown in the UI and available\nto AI agents for context; has no effect on execution.","maxLength":5120},"_connectionId":{"format":"objectId","type":["string","null"],"description":"Connection this export uses to reach the source system. The connection's type must be\ncompatible with the export's `adaptorType` (e.g. an `HTTPExport` needs an `http` connection;\n`NetSuiteExport` and `NetSuiteHTTPExport` both need a `netsuite` connection).\nServer-required unless `type` is `webhook` or `simple` (those receive data instead of\nfetching it) or `adaptorType` is `TopicExport` (Event Streams listeners are delivered\nby the platform itself and must omit it). A `WebhookExport` saved without `type: webhook`\nis refused with 422 \"_connectionId is required unless type is [webhook|simple]\"; a webhook\nexport that does carry a connection saves and updates normally."},"adaptorType":{"type":"string","description":"Selects the adaptor technology that executes this export, which determines the compatible\nconnection types and which adaptor-specific configuration object must also be supplied\n(e.g. set `salesforce` when using `SalesforceExport`).","enum":["HTTPExport","FTPExport","AS2Export","S3Export","NetSuiteExport","NetSuiteHTTPExport","SalesforceExport","JDBCExport","RDBMSExport","MongodbExport","DynamodbExport","WrapperExport","SimpleExport","WebhookExport","FileSystemExport","TopicExport","RESTExport"]},"nsDomainType":{"type":"string","enum":["suitetalk","restlet"],"description":"Selects which NetSuite REST host a `NetSuiteHTTPExport` calls. The server derives the\nfull base URL from the connection's NetSuite account\n(`https://<account>.suitetalk.api.netsuite.com` or `https://<account>.restlets.api.netsuite.com`)\nand signs each request with the connection's token-based credentials, so `http.relativeURI`\ncarries the complete path starting at `/services/rest/...` or `/app/site/hosting/restlet.nl`.\nIgnored on other adaptor types."},"type":{"type":["string","null"],"description":"Operational mode of the export. When omitted, the export retrieves all available records —\nthe standard batch mode, which is also the right choice for parsing structured files\n(CSV/XML/JSON) into records. Most modes take their matching configuration object (e.g. a\n`webhook` object when `type` is `webhook`); an HTTP or SQL `delta` export saves without a\n`delta` object because its request carries `{{lastExportDateTime}}`, though\n`delta.startDate` sets its first window. It still needs `type: delta`: on an export\nwith no `type`, `{{lastExportDateTime}}` and `{{currentExportDateTime}}` render\nempty on every run, so a request that carries them goes out with blank values each time\n(live-verified 2026-10-22). Use `blob` only to transfer raw files without parsing their\ncontents.","enum":["webhook","test","delta","once","tranlinedelta","simple","blob","distributed","stream","all"]},"pageSize":{"type":["integer","null"],"description":"Number of records per page streamed to downstream flow steps when the export is the\nflow's source step. Pages are additionally capped at 5 MB regardless of record count, so\nvery large records may produce smaller pages. The server does not validate the value.\nA lookup (`isLookup: true`) runs to completion and hands every result to the incoming\nrecord as one response, capped at 5 MB in total, so the value does not change how many\nresults a lookup returns; a result set larger than that belongs in a standalone export\nstep. In a test run or preview the value also caps the records fetched (default 20).\nOn a webhook listener the records of each incoming request are paged separately, before\nany hook or downstream step runs.","default":20},"dataURITemplate":{"type":"string","description":"Handlebars template that builds a link back to each record in the source application's UI\n(e.g. `https://my.salesforce.com/lightning/r/Contact/{{record.Id}}/view`). The resolved\nURL is stored with error records in job history so users can jump straight to the record."},"traceKeyTemplate":{"type":["string","null"],"description":"Handlebars template that overrides how each record's unique trace key is generated, used\nto track records through the flow and match retries to prior errors (e.g.\n`{{join \"_\" record.customerId record.orderId}}`). When omitted, the system picks a unique\nfield automatically from the record's top level, by exact field name (`GET\n/v1/flows/{_id}/traceKey` serves the pattern). The rendered value of a template is stamped\non each record as `io_custom_trace_key` and travels downstream with it. If a transform\nreshapes the data first, omit the `record.` prefix. Trace keys are capped at 256 characters;\na longer key is stored truncated from the middle, keeping the beginning and end of the\nvalue. Downstream imports use the trace key as their default concurrency lock key (a blank\nkey means no lock); it never de-duplicates — a record emitted twice with the same key is\nprocessed twice."},"skipRetries":{"type":"boolean","description":"When true, the platform does not retain the source data needed to retry failed records,\nreducing stored data at the cost of being unable to reprocess errors from this export.\nShown as \"Do not store retry data\" in the UI. Defaults to false."},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"isLookup":{"type":"boolean","description":"When true, the export runs as a mid-flow lookup: it executes once per incoming record,\nusing the input record's fields to parameterize the request, and passes the results to\nsubsequent steps. When false, the export runs as a standalone data source. A lookup's\nresults are not paged through the flow: every page the adaptor fetches is merged into one\nresponse for the record, up to 5 MB, so `pageSize` does not limit or extend what a lookup\nreturns. A lookup that must return more than that is better modeled as a standalone\nexport step."},"groupByFields":{"$ref":"#/components/schemas/GroupBy"},"delta":{"$ref":"#/components/schemas/Delta"},"test":{"$ref":"#/components/schemas/Test"},"once":{"$ref":"#/components/schemas/Once"},"webhook":{"$ref":"#/components/schemas/Webhook"},"simple":{"$ref":"#/components/schemas/Simple"},"distributed":{"$ref":"#/components/schemas/Distributed"},"cdc":{"$ref":"#/components/schemas/Cdc"},"topic":{"$ref":"#/components/schemas/Topic"},"filesystem":{"$ref":"#/components/schemas/FileSystem"},"http":{"$ref":"#/components/schemas/Http"},"file":{"$ref":"#/components/schemas/File"},"salesforce":{"$ref":"#/components/schemas/Salesforce"},"as2":{"$ref":"#/components/schemas/AS2"},"dynamodb":{"$ref":"#/components/schemas/DynamoDB"},"ftp":{"$ref":"#/components/schemas/FTP"},"jdbc":{"$ref":"#/components/schemas/JDBC"},"mongodb":{"$ref":"#/components/schemas/MongoDB"},"netsuite":{"$ref":"#/components/schemas/NetSuite"},"rdbms":{"$ref":"#/components/schemas/RDBMS"},"s3":{"$ref":"#/components/schemas/S3"},"wrapper":{"$ref":"#/components/schemas/Wrapper"},"parsers":{"$ref":"#/components/schemas/Parsers"},"filter":{"description":"Filter applied to each record after the export's transform and before its preSavePage\nhook. Records that match continue through the flow; records that don't are silently\ndropped. Filter expressions reference the transformed record's fields.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"inputFilter":{"description":"Filter applied to incoming records before a lookup export queries the external system.\nOnly matching input records trigger lookup calls; non-matching records pass through the\nstep unenriched, which cuts unnecessary API traffic. Only relevant when `isLookup` is true.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"mappings":{"description":"Field mappings applied to each incoming record before a lookup HTTP request is made,\nreshaping the upstream record into the structure the lookup target API expects. Only\nsupported when `isLookup` is true and `adaptorType` is `HTTPExport` or\n`NetSuiteHTTPExport`; do not set it on source exports or other lookup adaptors.","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"transform":{"description":"Transformation that reshapes records before they leave this step — source exports\ntransform the retrieved records, lookup exports transform the lookup results. Commonly\nused to flatten nested lookup responses (e.g. `results[0].id` → `id`); merging results\nback into source records is handled separately by the flow's response mapping.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the export lifecycle for\ntransformations, validation, and filtering beyond what configuration alone can express.\nA hook name not listed here (an import's `preMap`, for example) is dropped on save\nwithout an error, so read `hooks` back after saving.","properties":{"preSavePage":{"type":"object","description":"Hook that runs on each page of records after the export's transform and output\nfilter, before the page is sent to downstream steps; commonly used to flatten\nstructures, drop unwanted records, or add computed fields. It receives the page as\n`options.data` (with `errors`, `pageIndex`, `configuration` and `settings`) and returns\n`{data, errors, abort, newErrorsAndRetryData}`. Return that object, not the array: a\nhook that returns `options.data` itself, or a filtered copy, drops the page without an\nerror, and the run completes with 0 records, 0 errors and 0 pages. An exception thrown\nhere fails every record on that page, each with its own retry data, and later pages\nstill run.","properties":{"function":{"type":"string","description":"Function to invoke within the referenced script."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`. Must be a script in this\naccount: any other id fails the save with 422 (\"Only a script owned by the user or\na script shared by the Integration App can be used at hooks.preSavePage._scriptId.\")."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments."},"configuration":{"type":"object","description":"Static parameters passed to the hook function at runtime, letting one script be\nreused across exports with different settings."}}},"preParse":{"type":"object","description":"Hook that runs on file-based exports after the file is downloaded and before it is\nparsed: it receives the raw file text as `preParseData` together with `fileMeta` and\nreturns both, modified or not, for the configured parser to read. Used to normalize a\nnon-standard file — fix a header row, strip a preamble, convert a format — before\nparsing; an exception thrown here fails the run.","properties":{"function":{"type":"string","description":"Function to invoke within the referenced script."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments."},"configuration":{"type":"object","description":"Static parameters passed to the hook function at runtime, letting one script be\nreused across exports with different settings."}}}}},"settingsForm":{"$ref":"#/components/schemas/Form"},"settings":{"$ref":"#/components/schemas/Settings"},"mockOutput":{"$ref":"#/components/schemas/MockOutput"},"_ediProfileId":{"type":"string","format":"objectId","description":"EDI profile this export uses to parse incoming X12 or EDIFACT documents into structured\nJSON — it supplies the envelope qualifiers, delimiters, version, and validation rules.\nSet it when the export parses or validates EDI files (a save with a `delimited/x12` file\ndefinition at `version` `\"2\"` and no profile fails with 422 `missing_ediProfile`); omit\nit otherwise. Accepted on the file-reading exports — FTP, AS2, S3 and HTTP file mode\n(`http.type: \"file\"`)."},"_postParseListenerId":{"type":"string","format":"objectId","description":"Webhook export invoked once per file after EDI parsing, on both success and failure\n(errors are included in the payload when parsing fails). Primarily used to send\nfunctional acknowledgements (997/999) to trading partners. Accepted on the file-reading\nexports that parse EDI — FTP, AS2, S3 and HTTP file mode (`http.type: \"file\"`)."},"externalId":{"type":["string","null"],"description":"External identifier for correlating the export with a record in another system."},"_integrationId":{"type":["string","null"],"format":"objectId","description":"Not stored on an export: a value sent on create or on a full update is dropped, and a GET\ndoesn't return the field. An export isn't owned by an integration; to find the flows and\nother resources that use it, call `GET /v1/exports/{_id}/dependencies`."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this export was created from, set when the export is part of an installed\nintegration app. The app's install sets it: a create that sends it is refused with 403\n(\"Cannot create an Integration App resource\")."},"unencrypted":{"type":"object","description":"Custom configuration values stored without encryption and returned in API responses."},"useTechAdaptorForm":{"type":"boolean","description":"When true, the UI presents the full technical adaptor form for this export instead of\nthe simplified assistant form."},"rawData":{"type":"string","description":"Key referencing the raw sample payload captured for preview and testing."},"preSave":{"$ref":"#/components/schemas/PreSave"},"assistant":{"type":"string","description":"Identifier for the connector assistant used to configure this export. For the cloud\nfile providers it also selects the provider-specific location rules —\n`azuredatalakestoragegen2` (container in `file.directory.name`, path in\n`http.relativeURI`) and `googlecloudstorage` (bucket in `file.directory.storageRootId`,\nprefix in `file.directory.id`)."},"assistantMetadata":{"type":["object","string"],"additionalProperties":true,"description":"Metadata associated with the connector assistant configuration."},"sampleData":{"type":["string","object","array","null"],"description":"Sample data payload used for previewing and testing the export."},"sampleHeaders":{"type":"array","description":"Sample HTTP headers used for previewing and testing the export.","items":{"type":"object","properties":{"name":{"type":"string","description":"Header name."},"value":{"type":"string","description":"Header value."}}}},"sampleQueryParams":{"type":"array","description":"Sample query parameters used for previewing and testing the export.","items":{"type":"object","properties":{"name":{"type":"string","description":"Query parameter name."},"value":{"type":"string","description":"Query parameter value."}}}}}},"OneToMany":{"type":"boolean","description":"When true, the step runs once per child record instead of once per incoming record.\n`pathToMany` names the array field that holds the children inside an object record; when\nthe incoming record is itself an array (grouped or row-based data), leave `pathToMany` blank\nand each element becomes a record. The fan-out is scoped to this step: afterwards the children\nare re-joined into the record's original shape — the object with its array, or the array of rows —\ncarrying any response-mapping enrichment, and that re-joined record is what the next step\nreceives. Not for locating records in an export's HTTP response; use\n`http.response.resourcePath` for that. Applies to steps that receive a record — imports and\nlookups (an export referenced as a page processor of a flow, API, or Tool). A standalone export\n(a flow's page generator) has no incoming record, so the fields are saved but have no effect on\nthe records it produces; to emit one record per array element from a standalone export, use a\n`hooks.preSavePage` script, or for a file-definition export make the repeating segment the\nrecord boundary in the definition's rules.\n","default":false},"PathToMany":{"type":"string","description":"Path to the array of child records inside an object record when `oneToMany` is true, in dot\nnotation (`items`, `lines.lineItems`). Leave blank when the incoming record is itself an array\n(grouped or row-based data) — each element is then a child record. A path that does not resolve\nto an array processes zero records and reports success. Read only on steps that receive a\nrecord (imports, lookup exports); on a standalone export it is saved and ignored — see `oneToMany`.\n"},"GroupBy":{"type":"array","description":"Specifies which fields to use for grouping records in the export results. When configured, records with\nthe same values in these fields will be grouped together and treated as a single record by downstream\nsteps in your flow.\n\nFor example:\n- Group sales orders by customer ID to process all orders for each customer together\n- Group journal entries by accounting period to consolidate related transactions\n- Group inventory items by location to process inventory by warehouse\n\nWhen grouping is used, the export's page size determines the maximum number of groups per page, not individual\nrecords. Note that effective grouping typically requires that records with the same group field values appear\ntogether in the export data.\n\nNot a webhook listener feature: on a `webhook` export the platform applies it only within each page, after\nthe request has been paged, and only to adjacent rows — the field is not offered for listeners in the UI.\n","items":{"type":"string"}},"Delta":{"type":"object","description":"Configures incremental exports that retrieve only records created or modified since the\nflow's delta checkpoint; omit it for other export types. Adaptors that compare a record field\nname it in `dateField`, while HTTP exports and SQL queries carry {{lastExportDateTime}} in the\nrequest and save without a delta object. Each run's window runs from {{lastExportDateTime}}\n(the checkpoint) to {{currentExportDateTime}} (the time the run started). A first run is not a\nfull export: with no `startDate`, its {{lastExportDateTime}} is the run's own timestamp, so it\npicks up no earlier changes. The checkpoint moves to a run's window end only when the run\nexports records, so a run that exports nothing leaves it where it was, and after a failed run\nthe next run reuses the last checkpoint. On success, when `POST /v1/flows/{_id}/run` sends\n`export.endDate`, the flow's checkpoint is left at that value, so an end date in the past\nmoves the next run's lower bound back.","properties":{"dateField":{"type":"string","description":"Record timestamp field(s) compared against the last successful run time to identify\nchanged records. Accepts a single field or multiple comma-separated fields, processed\nsequentially — useful when different operations update different timestamp fields.\nIf the flow's own downstream steps update the exported records, add export criteria\nthat exclude already-processed records (or use a creation-time field for process-once\nflows) so each run doesn't re-export what the previous run wrote. Not supported on\nHTTP exports: embed {{lastExportDateTime}} in the relativeURI or body instead; an HTTP\nexport saved with dateField is refused with 422 \"Unsupported dateField in\nexport.delta.dateField\". For Salesforce this\nfield is required and defaults to the standard timestamp fields (LastModifiedDate,\nCreatedDate, SystemModstamp, LastActivityDate, LastViewedDate, LastReferencedDate) plus\nany custom timestamp fields."},"dateFormat":{"type":"string","description":"Moment.js format string applied to both delta variables, {{lastExportDateTime}} and\n{{currentExportDateTime}}, in HTTP requests and SQL queries alike. Leave unset unless the\nsource system requires a non-ISO8601 format; by default both render as ISO 8601 UTC with\nmilliseconds. Date-only formats truncate the time portion, widening the filter window."},"lagOffset":{"type":"integer","description":"Buffer in milliseconds subtracted from the last successful run timestamp, creating an\noverlapping window that catches records still propagating when the previous run executed.\nSet it when records created or modified near the run time are occasionally skipped due to\nreplication or indexing delays. Keep it as low as possible — larger values reprocess\nredundant records. A negative value shifts the window forward (look-ahead) instead of back."},"startDate":{"type":["string","null"],"format":"date-time","description":"Lower-bound cutoff for the first run. Omitting it doesn't export history: the first run\nthen starts at its own timestamp, an empty window. Set it to the earliest change the first\nrun should pick up. Later runs start from the flow's checkpoint, the end of the last run\nthat exported records."}}},"Test":{"type":"object","description":"Configures test exports that cap the number of records retrieved, for safely developing and\nvalidating against small data samples. Required when the export's type is \"test\"; omit for\nother export types. A test export behaves like a standard export in every other way (filters,\npagination, processing) and stores no state between runs.","properties":{"limit":{"type":"integer","default":1,"description":"Caps the total records a test run processes, counted on top-level records before\noneToMany processing; the maximum exists to prevent accidentally processing large\ndatasets during development. The cap applies across pages — processing stops once the\nlimit is reached regardless of pageSize. When transitioning to production, leave this in\nplace and change the export's type field; the limit then no longer applies.","minimum":1,"maximum":100}}},"Once":{"type":"object","description":"Configures flag-based exports that skip records an earlier run already processed. Required\nwhen the export's type is \"once\"; omit for other export types. Each run retrieves records\nwhere the tracking boolean field is false, then sets that field to true after successful\nprocessing so later runs skip them; if a run fails, the flags are left unchanged and the\nrecords are retried automatically on the next run.","properties":{"booleanField":{"type":"string","description":"API field name of the boolean/checkbox in the source system that tracks processed\nrecords: the export selects only records where this field is false, then sets it to true\nin batches after each successfully processed page. The field must be writeable by the\nexport's connection. Ensure no other process updates the same field — use a separate\nflag per export process — or records may be skipped unexpectedly."}}},"Webhook":{"type":"object","description":"Configuration for webhook listeners that receive data through incoming HTTP requests. Required when the export's `type` is `webhook`; omit for other export types. Senders POST to the export's listener URL, `https://api.integrator.io/v1/exports/{_id}/data` (or the same path on the account's regional API host); the platform validates each request using the configured `verify` method, passes the payload to subsequent flow steps, and returns a configurable HTTP response to the caller. A request is answered 204 by default when accepted, 401 when it fails verification, and 403 `unable_to_run_flows` while every flow the export feeds is disabled; a refused request is not queued, so the sender must resend it.","properties":{"provider":{"type":"string","description":"Source application sending the webhook, used to pre-configure security settings and payload parsing for that platform. Use `custom` (the default when omitted) for unlisted providers or when you need full manual control over the security configuration. Provider-specific choices may require credentials (tokens, keys) mandated by that platform.","enum":["github","shopify","travis","travis-org","slack","dropbox","onfleet","helpscout","errorception","box","stripe","aha","jira","pagerduty","postmark","mailchimp","intercom","activecampaign","segment","recurly","shipwire","surveymonkey","parseur","mailparser-io","hubspot","integrator-extension","custom","sapariba","happyreturns","typeform"]},"verify":{"type":"string","description":"Verification method applied to every incoming request before processing; required for all webhook exports. Each method needs companion fields: `hmac` requires `key`, `algorithm`, `encoding`, and `header` (except on connector-backed webhooks, where the connector definition supplies them); `token` requires `token`, with `tokenLocation` defaulting to `body` and selecting which location-specific field applies; `basic` requires `username` and `password`; `secret_url` requires `token`. Prefer `hmac` when the source system supports it — it is the most secure option; `secret_url` relies only on URL obscurity and suits non-sensitive data or testing.","enum":["token","hmac","basic","secret_url"]},"token":{"type":"string","description":"Shared secret used when `verify` is `token` or `secret_url`. For `token` verification, the value found at `path` in each request must exactly match (case- and whitespace-sensitive) or the request is rejected with a 401. For `secret_url`, the token is embedded in the webhook URL to create a hard-to-guess endpoint — generate a random, high-entropy value and treat it as a sensitive credential."},"algorithm":{"type":"string","description":"Hashing algorithm used to validate HMAC signatures when `verify` is `hmac`. Must match the algorithm the webhook sender uses — a mismatch causes every request to be rejected. Use `sha256` unless the provider explicitly requires another value.","enum":["sha1","sha256","sha384","sha512"]},"encoding":{"type":"string","description":"Encoding of the HMAC signature value when `verify` is `hmac`. Must match the encoding the webhook sender uses — a mismatch causes requests to be rejected with a 401 even when the signature is otherwise correct. With `provider: custom`, `hex` is compared as lowercase hex: an uppercase hex signature is rejected.","enum":["hex","base64"]},"key":{"type":"string","description":"Secret used to validate signatures when `verify` is `hmac`. The system computes a signature of the request body using this key and the configured `algorithm`, then compares it with the signature sent in the request header named by `header`. Treat it as a highly sensitive credential — never expose it in repositories or logs."},"header":{"type":"string","description":"Name of the request header that carries the HMAC signature when `verify` is `hmac`. Must match the header name the webhook sender uses (header names are case-insensitive); requests without this header are rejected with a 401. With `provider: custom` the header value must be the bare digest in the configured `encoding`: a value with a `sha256=` prefix is rejected with a 401."},"tokenLocation":{"type":"string","enum":["body","header","queryParam"],"default":"body","description":"Where the verification token is found when `verify` is `token`. Each location uses a different companion field: `body` uses `path`, `header` uses `tokenHeaderName` (and `tokenHeaderScheme`), `queryParam` uses `tokenQueryParamName`."},"path":{"type":"string","description":"JSON path into the request body holding the verification token when `verify` is `token` and `tokenLocation` is `body` (e.g. `meta.token`). The value at this path must exactly match `token` or the request is rejected."},"tokenHeaderName":{"type":"string","description":"Request header carrying the verification token when `verify` is `token` and `tokenLocation` is `header`."},"tokenHeaderScheme":{"type":"string","enum":["bearer","custom","none"],"default":"bearer","description":"Scheme prefixing the token in the header when `tokenLocation` is `header`."},"customTokenScheme":{"type":"string","description":"Custom header scheme prefix used when `tokenHeaderScheme` is `custom`."},"tokenQueryParamName":{"type":"string","description":"Query parameter carrying the verification token when `verify` is `token` and `tokenLocation` is `queryParam`."},"_httpConnectorId":{"type":"string","format":"objectId","description":"HTTP connector backing this listener when the webhook is provided by an assistant/connector (e.g. Slack); set by the platform for connector-backed listeners."},"requestMediaType":{"type":"string","enum":["json","xml","csv","urlencoded","plaintext"],"description":"Overrides how the incoming request body is parsed when the provider sends a non-standard content type. When omitted, the body is parsed by its Content-Type header."},"pathToRecords":{"type":"string","description":"JSON path into an object payload to the array whose elements become the records. When omitted, a top-level JSON array is emitted as one record per element and an object payload is emitted as a single record."},"includeParentData":{"type":"boolean","description":"When true and `pathToRecords` is set, each emitted record also carries the surrounding parent fields from the payload."},"username":{"type":"string","description":"Username half of the credentials when `verify` is `basic`. Incoming requests must include an `Authorization: Basic` header carrying the base64-encoded `username:password` pair. Use only over HTTPS to prevent credential interception."},"password":{"type":"string","description":"Password half of the credentials when `verify` is `basic`, validated together with `username` from the request's `Authorization` header. Use a strong, unique value and treat it as a sensitive credential."},"successStatusCode":{"type":"integer","description":"HTTP status code returned to the caller after successful processing; must be a valid 2xx code. The default 204 returns no response body and causes `successBody` to be ignored — set 200 or 202 when the caller needs a response body or expects a specific code.","default":204},"successBody":{"type":"string","description":"Response body returned to the caller after successful processing; ignored when `successStatusCode` is 204. Content type is set by `successMediaType`, and the value can be static text or structured JSON/XML, including handlebars expressions for dynamic values."},"successMediaType":{"type":"string","description":"Sets the Content-Type header on successful webhook responses. Only takes effect when a `successBody` is returned (a status code other than 204), and must match the actual format of that body.","default":"json","enum":["json","xml","plaintext"]},"successResponseHeaders":{"type":"array","description":"Custom headers added to successful webhook responses — for example CORS headers or correlation IDs. Headers defined here take precedence over automatically set headers such as Content-Type, and values support handlebars expressions for dynamic content.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the header to set on successful webhook responses; headers defined here take precedence over automatically set headers such as Content-Type."},"value":{"type":"string","description":"Value sent for the header; supports handlebars expressions for dynamic content."}}}},"challengeResponseHeaders":{"type":"array","description":"Custom headers returned for webhook subscription verification (challenge) requests, which providers send before delivering real events. Required header values vary by provider — consult the provider's documentation, since incorrect challenge headers can prevent the subscription from being verified.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the header to set on challenge (subscription verification) responses."},"value":{"type":"string","description":"Value sent for the header; consult the provider's documentation for required challenge header values."}}}},"challengeSuccessBody":{"type":"string","description":"Response body returned for webhook subscription verification (challenge) requests. Many providers require echoing back a challenge value from the request, which handlebars expressions can access — for example `{{hub.challenge}}` (Facebook/Instagram) or `{\"challenge\":\"{{challenge}}\"}` (Slack). An incorrect challenge response prevents the subscription from being verified."},"challengeSuccessStatusCode":{"type":"integer","description":"HTTP status code returned for webhook subscription verification (challenge) requests. Most providers expect the default 200; change it only when the provider's verification explicitly requires a different code.","default":200},"challengeSuccessMediaType":{"type":"string","description":"Sets the Content-Type header on challenge responses. Must match both the format of `challengeSuccessBody` and the provider's requirements — for example, Slack expects `json` while Facebook/Instagram verification expects `plaintext`.","default":"json","enum":["json","xml","plaintext"]}},"if":{"properties":{"verify":{"const":"hmac"}},"required":["verify"],"not":{"required":["_httpConnectorId"]}},"then":{"required":["key","algorithm","encoding","header"]},"else":{"if":{"properties":{"verify":{"const":"token"}},"required":["verify"]},"then":{"required":["token"],"if":{"properties":{"tokenLocation":{"const":"body"}},"required":["tokenLocation"]},"then":{"required":["path"]},"else":{"if":{"properties":{"tokenLocation":{"const":"header"}},"required":["tokenLocation"]},"then":{"required":["tokenHeaderName"]},"else":{"if":{"properties":{"tokenLocation":{"const":"queryParam"}},"required":["tokenLocation"]},"then":{"required":["tokenQueryParamName"]},"else":{"required":["path"]}}}},"else":{"if":{"properties":{"verify":{"const":"basic"}},"required":["verify"]},"then":{"required":["username","password"]},"else":{"if":{"properties":{"verify":{"const":"secret_url"}},"required":["verify"]},"then":{"required":["token"]}}}}},"Simple":{"type":"object","description":"Configuration for `simple` (data-loader) exports — exports that accept manually uploaded\nfiles through the data-loader UI instead of fetching data from a connection. Relevant only\nwhen the export's `type` is `simple`; the uploaded content is parsed using the same file\nsettings as file-based exports.","properties":{"file":{"$ref":"#/components/schemas/File"}}},"File":{"type":"object","description":"Controls how files are parsed, filtered, and processed across all file-based exports\n(FTP/SFTP, Amazon S3, simple file uploads, and other file sources). The type field selects\nthe file format and determines which format-specific object (csv, json, xlsx, xml, or\nfileDefinition) must be configured; the output field selects whether files are parsed into\nrecords, transferred as blobs, or listed as metadata only. The filter object selectively\nskips files before processing.","properties":{"encoding":{"type":"string","description":"Character encoding used to read and parse file content. If the encoding is unknown, try\nutf8 first (the default), then win1252 for Western-language files with garbled\ncharacters; consider the geographic origin of the data when selecting.","enum":["utf8","win1252","utf-16le","gb18030","macroman","iso88591","shiftjis"]},"type":{"type":"string","description":"Format of the files being processed; determines which format-specific configuration\nobject (csv, json, xlsx, xml, or fileDefinition) must be populated — other format\nobjects are ignored. Required for all file-based exports except blob exports (export\ntype \"blob\" or output \"blobKeys\").","enum":["csv","json","xlsx","xml","filedefinition"]},"output":{"type":"string","description":"Processing mode for retrieved files: parse contents into records, transfer files as\nunparsed blobs, or return only file metadata. Determines what data is passed to\nsubsequent flow steps.","enum":["records","metadata","blobKeys"]},"skipDelete":{"type":"boolean","default":false,"description":"When true, source files remain on the file server after processing; when false (the\ndefault), files are deleted after successful processing. Files that fail processing are\nnever deleted, regardless of this setting. Enable retention when files must be processed\nby other flows or systems, or kept for compliance."},"compressionFormat":{"type":"string","description":"Compression format of incoming files, which are decompressed before any other processing\n(parsing, filtering). Set this only when the source always delivers compressed files —\nif a file marked as compressed is not actually compressed, processing fails. Leave unset\nwhen files arrive uncompressed or only sometimes compressed.","enum":["gzip","zip"]},"purgeInternalBackup":{"type":"boolean","description":"When true, Celigo keeps no internal backup copies of files processed by this export;\nwhen false (the default), copies are retained for your account's retention period and\nare available for reprocessing or troubleshooting. Applies only to this export and only\nto Celigo's internal copies — source files are governed by skipDelete. Enable for highly\nsensitive data or zero-retention policies; without backups, recovery may require\nre-obtaining files from the source system."},"decrypt":{"type":"string","description":"Decryption applied to incoming files before any other processing; decryption runs before\ndecompression, and a decryption failure fails the file's processing entirely. The\nconnection must already be configured with the private key (and passphrase, if\napplicable) matching the public key used to encrypt the files. Only PGP/GPG encryption\nis currently supported.","enum":["pgp"]},"batchSize":{"type":["integer","null"],"description":"Number of files retrieved per batch; if a batch fails, the whole batch is retried\n(stored as `null` when not configured). Use\nlower values (10-50) for large files to reduce timeout and memory pressure, and higher\nvalues for many small files to improve throughput. Controls file retrieval only — record\npaging is governed by the export's pageSize.","maximum":1000},"sortByFields":{"type":"array","description":"Sorts the records parsed from each file before they are processed, establishing a\ndeterministic processing order (for example, chronological or priority-based). Sorting\nhappens after parsing but before any filtering or grouping, and does not modify the\nsource files. Sorting by the same fields used in groupByFields improves grouping\nperformance.","items":{"type":"object","properties":{"field":{"type":"string","description":"Record field to sort by; use dot notation for nested properties (e.g.\ncustomer.name). Field names are case-sensitive."},"descending":{"type":"boolean","description":"When true, sorts this field in descending order (newest/highest first); when false\nor omitted, sorts ascending. Directions can be mixed across fields in a\nmulti-field sort."}}}},"groupByFields":{"$ref":"#/components/schemas/GroupBy"},"groupEmptyValues":{"type":"boolean","description":"When true, records whose groupByFields values are empty are still grouped together rather\nthan each forming its own group; when false (the default), empty-keyed records are not\ngrouped. Only relevant when groupByFields is set."},"csv":{"type":"object","description":"Parsing settings for delimiter-separated text files (CSV, TSV, pipe-delimited, and\nsimilar). Configure when type is \"csv\".\n\nRequired when type is csv.","properties":{"columnDelimiter":{"type":"string","description":"Character sequence separating fields within each row; comma when omitted. Use \"\\t\"\nfor tab-delimited files; European-locale exports often use semicolons. A wrong\ndelimiter doesn't fail the parse: with a header row, each row becomes one field named\nafter the whole header line (`{\"order_id;sku;qty\": \"1001;TEE-M;2\"}`), so the failure\nshows up in later steps."},"rowDelimiter":{"type":"string","description":"Character sequence marking the end of each record; auto-detected from file content\nwhen omitted. Set explicitly (\"\\n\", \"\\r\\n\", or \"\\r\") only when auto-detection fails,\nsuch as for files with mixed line endings — an incorrect value merges or splits\nrecords."},"hasHeaderRow":{"type":"boolean","description":"When true (the default), the first row is read as field names rather than data, and\nthose names are available in mappings. When false, every row is treated as a data\nrecord and fields are referenced by position."},"trimSpaces":{"type":"boolean","description":"When true, removes leading and trailing whitespace from every field value during\nparsing; when false (the default), whitespace is preserved exactly as in the source.\nHeader row values are always trimmed regardless of this setting, and spaces between\nwords are never affected."},"rowsToSkip":{"type":"integer","description":"Number of rows at the top of the file to ignore before parsing begins — useful for\nreport titles, timestamps, or other metadata above the data. The header row (when\nhasHeaderRow is true) is expected after the skipped rows."},"disableQuoteAndStripEnclosingQuotes":{"type":"boolean","description":"When true, disables CSV quote processing: quotes are treated as literal characters,\nenclosing quotes are stripped, and delimiters inside quoted text split the field.\nWhen false (the default), standard RFC 4180 quoting applies and quoted fields\nprotect embedded delimiters. Enable only for files with non-standard or malformed\nquoting; review this setting first when field counts vary unexpectedly between rows."},"keyColumns":{"type":"array","description":"Field names whose values together identify a record's group when consecutive rows\nshare the same key — used to merge multi-line records (e.g. an order header repeated\nacross line-item rows) into a single record. Leave empty for one record per row; the\ncolumns must exist in the parsed header.","items":{"type":"string"}}}},"json":{"type":"object","description":"Parsing settings for JSON files. Configure when type is \"json\". resourcePath locates the\narray of records when they are nested inside a container object; malformed JSON fails\nthe entire file's processing.\n\nRequired when type is json.","properties":{"resourcePath":{"type":"string","description":"Dot-notation path to the array of records within the JSON structure (e.g.\n\"response.data.customers\"); leave empty when the file's root is already the record\narray. The path must resolve to an array of objects — array indexing and wildcards\nare not supported. A path that resolves to nothing produces zero records without\nraising an error."}}},"xlsx":{"type":"object","description":"Parsing settings for Microsoft Excel (.xlsx) workbooks. Configure when type is \"xlsx\".\nCalculated cell values are extracted rather than formulas; legacy .xls files are not\nsupported — use the modern Open XML format.\n\nRequired when type is xlsx.","properties":{"hasHeaderRow":{"type":"boolean","description":"When true (the default), the first row is read as field names rather than data;\nblank header cells are auto-named and duplicate names are made unique with suffixes.\nWhen false, every row is treated as data and fields receive generic positional names\n(Column1, Column2, ...)."}}},"xml":{"type":"object","description":"Parsing settings for XML documents. Configure when type is \"xml\". resourcePath is an\nXPath expression selecting the elements treated as records; namespaces are handled\nautomatically.\n\nRequired when type is xml.","properties":{"resourcePath":{"type":"string","description":"XPath expression selecting the elements treated as records — each matching element\nbecomes one record, with its child elements as fields. Required for XML parsing;\nthere is no default. Use an absolute path (/Root/Order) when the structure is fixed,\n//Element to match at any depth, or predicates (//Element[@type=\"product\"]) to\nfilter; XPath is case-sensitive, and a non-matching path yields zero records."}}},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"},"fileDefinition":{"type":"object","description":"Parsing via a predefined file definition resource, for formats the standard parsers\ncannot handle — fixed-width files, EDI documents (X12, EDIFACT), and multi-record-type\nor proprietary formats. Configure when type is \"filedefinition\".\n\nRequired when type is filedefinition.","properties":{"_fileDefinitionId":{"type":"string","format":"objectId","description":"File definition resource containing the parsing rules for this format. Must\nreference an existing, accessible file definition — never guess or fabricate the ID.\nDefinitions are reusable across exports, so changing one affects every export that\nuses it."},"allowPartialSuccess":{"type":"boolean","description":"When true, a file whose records partially fail parsing against the file definition\nstill emits the records that did parse, instead of failing the entire file. Applies to\nfile-definition (fixed-width, EDI) parsing only. For X12 and EDIFACT the unit is the\ntransaction set (ST–SE, UNH–UNT), and it takes effect only with an EDI profile linked:\na failed set is set aside and reported as an error while the file's other sets go on\nas records. Without this flag, one failed set fails the whole file."}}},"filter":{"description":"Selects which files are processed: files matching the filter criteria are exported,\nand non-matching files are skipped entirely before processing begins. Filterable\nfields come from each file's listing entry: the name as `fileName`, the size as\n`filesize`, and the modified time as `lastModifiedTime` on FTP, SFTP and S3 or\n`modifiedTime` on cloud-storage folders (`http.type: \"file\"`)\n(e.g. [\"endswith\", [\"string\", [\"extract\", \"fileName\"]], \".csv\"]). There is no\n`lastmodified` key.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"backupPath":{"type":"string","description":"Path where backup copies of source files are stored before processing."},"includeSubdirectories":{"type":"boolean","default":false,"description":"When true, an Azure Data Lake Storage Gen2 transfer (`assistant: azuredatalakestoragegen2`,\n`http.type: file`) also picks up files in the subdirectories below the directory path in\n`http.relativeURI`; when false or omitted only that directory's own files are transferred."},"directory":{"type":"object","required":["pathMode"],"description":"Structured source location for cloud file-provider sources (Google Drive shared drives,\nBox, Dropbox, Google Cloud Storage, Azure Data Lake Storage Gen2), replacing the flat path\nused by classic FTP/S3 sources. Select the location by folder ID or by a path relative to\na configured storage root via pathMode. For the drive-style providers, directoryId mode\naddresses the folder by id alone — the flat relative-path field is not consulted at\nruntime. Google Cloud Storage puts the bucket in `storageRootId` and the object prefix in\n`id` with `pathMode: directoryId`; Azure Data Lake Storage Gen2 puts the container in\n`name` and the directory path in `http.relativeURI`.","properties":{"pathMode":{"type":"string","enum":["relativePath","directoryId"],"description":"How the directory is addressed — by provider folder ID, or by a path relative to a configured storage root."},"id":{"type":"string","description":"Provider-native folder ID of the source directory (e.g. a Google Drive folder ID); set\nwhen pathMode is directoryId. This is the file provider's own ID, not a Celigo resource ID.\nFor Google Cloud Storage it is the object prefix inside the bucket, ending with `/`."},"name":{"type":"string","description":"Display name of the folder identified by id, kept for readability in the UI; the\nfolder browser fills it when the location is picked visually. For Azure Data Lake\nStorage Gen2 it is the authoritative container name instead — the form saves the\ncontainer here and no id."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root (e.g. a Google Drive shared-drive ID) the relative\npath resolves against; set when pathMode is relativePath. The provider's own ID, not a Celigo ID.\nFor Google Cloud Storage it is the bucket name."},"storageRootName":{"type":"string","description":"Display name of the storage root identified by storageRootId, retained for reference in the UI."}}},"backupDirectory":{"type":"object","required":["pathMode"],"description":"Structured destination for backup copies of source files on cloud file providers — the\nfile-provider counterpart to backupPath. Addressed the same way as directory.","properties":{"pathMode":{"type":"string","enum":["relativePath","directoryId"],"description":"How the backup location is addressed — by provider folder ID, or by a path relative to a configured storage root."},"id":{"type":"string","description":"Provider-native folder ID of the backup directory; set when pathMode is directoryId."},"name":{"type":"string","description":"Display name of the backup folder identified by id, kept for readability in the UI;\nthe folder browser fills it when the location is picked visually."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root the backup relative path resolves against; set when pathMode is relativePath."}}}}},"IncludeParentData":{"type":"boolean","description":"When true and a path to records is configured on the export (`pathToRecords` for MongoDB and DynamoDB, `http.response.resourcePath` for HTTP, `file.json`/`file.xml` `resourcePath` for file-based exports), the data outside that path is attached to each extracted record as a `_PARENT` object, keeping the surrounding envelope available to downstream steps. When omitted or false, records are emitted without `_PARENT`, exactly as before. Constraints: on HTTP exports this option cannot be combined with `useXpathParser: true` (rejected at save with `EXPORT_INCLUDE_PARENT_DATA_XPATH_UNSUPPORTED`), and XML responses must resolve to the default converted-JSON parse mode — a run whose XML parser resolves to DOM/XPath mode (for example, because a connection-level field uses an advanced XPath expression) fails at start with the same error rather than silently omitting `_PARENT`. On file-based exports, it requires `file.type` of `json` with `file.json.resourcePath` configured — XML file envelope capture is not yet supported (the streaming XML parser emits no envelope event; XML support arrives with a parsers-package enhancement) — and any other file configuration is rejected at save with `EXPORT_INCLUDE_PARENT_DATA_UNSUPPORTED_FILE_TYPE`."},"Filter":{"type":"object","description":"Configuration for selectively processing records based on specified criteria. This object enables\nprecise control over which items are included or excluded from processing operations.\n\n**Filter behavior**\n\nWhen configured, the filter is applied before processing begins:\n- Items that match the filter criteria are processed\n- Items that don't match are completely skipped\n- No partial processing is performed\n\nSend the whole object. A rules array sent alone as the `filter` value is not\nstored, and a full update of an export that carries one removes the filter the\nexport had, with no error.\n\n**Implementation approaches**\n\nThere are two distinct filtering mechanisms available:\n\n**Rule-Based Filtering (`type: \"expression\"`)**\n- **Best For**: Common filtering patterns based on standard attributes\n- **Capabilities**: Filter by names, values, dates, numerical ranges, text patterns\n- **Advantages**: Declarative, no coding required, consistent performance\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear, static criteria for selection\n\n**Script-Based Filtering (`type: \"script\"`)**\n- **Best For**: Complex logic, dynamic criteria, or business rules\n- **Capabilities**: Full programmatic control, access to complete metadata\n- **Advantages**: Maximum flexibility, can implement any filtering logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Simple rules aren't sufficient or logic needs to be dynamic\n","properties":{"type":{"type":"string","description":"Determines which filtering mechanism to use. This choice affects which properties\nmust be configured and how filtering logic is implemented.\n\n**Available types**\n\n**Rule-Based Filtering (`\"expression\"`)**\n- **Required Config**: The `expression` object with rule definitions\n- **Behavior**: Evaluates declarative rules against item attributes\n- **Best For**: Common patterns like name matching, date ranges, value limits\n- **Advantages**: Simpler to configure, no custom code required\n\n**Script-Based Filtering (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to determine which items to process\n- **Best For**: Complex conditions, business logic, dynamic criteria\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard filtering needs (name, size, date), use `\"expression\"`\n2. For complex logic or conditions not covered by expressions, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based filtering. This object enables filtering\nitems based on common attributes without requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define filtering rules that\ncan match against item attributes like name, type, value, date, and other properties.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Rules can be combined with AND/OR logic\n- Each rule can check a specific attribute\n- Multiple conditions can be applied (ranges, pattern matching, exact matches)\n\n**Common filter patterns**\n\n1. **Pattern matching**: Regular expressions with `matches`\n2. **Value range filtering**: Numbers between min and max values\n3. **Date range filtering**: Items created/modified within specific time ranges\n4. **Status checking**: Items with specific status values or properties\n\nFor AI agents: Rule-based filtering should be your first choice when the filtering criteria\ncan be expressed in terms of standard attributes. Only use script-based filtering when\nmore complex logic is required.\n","properties":{"version":{"type":"string","description":"Version identifier for the expression format. Currently only version \"1\" is supported.\n\nThis field ensures future compatibility if the expression format evolves. Always set to \"1\"\nfor current implementations.\n","enum":["1"]},"rules":{"type":"array","description":"Expression array defining filter conditions using prefix notation. The first element is the operator,\nfollowed by its operands which may themselves be nested expression arrays.\n\nThe rule expression follows this pattern:\n- First element: Operator name (string)\n- Remaining elements: Operands for that operator (values or nested expressions)\n\n**Expression structure**\n\nFilter expressions use a prefix notation where operators appear before their operands:\n```\n[operator, operand1, operand2, ...]\n```\n\n**Comparison Operators**\n- `\"equals\"`: Exact match (equals)\n- `\"notequals\"`: Not equal to value (not equals)\n- `\"greaterthan\"`: Value is greater than specified value (is greater than)\n- `\"greaterthanequals\"`: Value is greater than or equal to specified value (is greater than or equals)\n- `\"lessthan\"`: Value is less than specified value (is less than)\n- `\"lessthanequals\"`: Value is less than or equal to specified value (is less than or equals)\n- `\"startswith\"`: String starts with specified prefix (starts with)\n- `\"notstartswith\"`: String does not start with specified prefix (does not start with)\n- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"notendswith\"`: String does not end with specified suffix (does not end with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"empty\"`: Field is missing or empty (is empty)\n- `\"notempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches a regular expression, ignoring case (matches)\n\nOperator names are case-insensitive: `NOTEMPTY` and `endsWith` work and are stored\nas sent. `isempty`, `isnotempty`, `anyof`, `notcontains`, `doesnotstartwith`,\n`doesnotendwith`, `notmatches`, `doesnotmatch` and `notequal` are not operators:\nthey are refused with 422 \"Invalid operation: <name>\", with the name in lowercase,\nboth when the filter is saved and by `POST /v1/processors/filter`. To negate a\ncondition, wrap it in `not`.\n\nThe string comparisons (`equals`, `contains`, `startswith`, `endswith` and their\nnegations) are case-sensitive; wrap the field in `lowercase` to compare without case.\n`matches` is not: every pattern is compiled with the flags `gi`, so `^paid$` also\nmatches `PAID` and `[a-z]` matches capital letters. An inline flag such as `(?i)`\nmakes the pattern invalid. The filter still saves, but in a run every record fails\nwith `FILTER_EVALUATION_FAILED` (\"Invalid regular expression: /(?i)…/gi: Invalid\ngroup\"), and `POST /v1/processors/filter` refuses the call with 422 (live-verified\n2026-10-22).\n\n**Logical Operators**\n- `\"and\"`: All conditions must be true\n- `\"or\"`: At least one condition must be true\n- `\"not\"`: Negates the condition\n\n**Field Access and Type Conversion**\n- `\"extract\"`: Access a field from the item by name\n- `\"settings\"`: Access a custom setting from the flow, flow step, or integration configuration\n- `\"boolean\"`: Convert value to Boolean type\n- `\"epochtime\"`: Convert value to Epoch Time (Unix timestamp)\n- `\"number\"`: Convert value to Number type\n- `\"string\"`: Convert value to String type\n\n**Field Access Details**\n\n**Using `extract` to access record fields:**\n- Retrieves values from the current record being processed\n- Can access nested properties using dot notation (e.g., `\"customer.email\"`)\n- Paths are relative to the record itself: `[\"extract\", \"record.status\"]`\n  matches nothing\n- In a record filter, must be wrapped in a type conversion (`string`, `number`,\n  `boolean` or `epochtime`): a bare `[\"extract\", ...]` operand is refused with 422\n  \"Field operation extract must be called by a Type Conversion Operation but was called\n  by <operator>\", both when an import's filter is saved and by\n  `POST /v1/processors/filter`\n\n**Using `settings` to access configuration values:**\n- Retrieves values from the integration's configuration settings\n- Supports different scopes with prefix notation:\n  - `flow.settingName`: Access flow-level settings\n  - `flowGrouping.settingName`: Access the flow group's settings\n  - `export.settingName`: Access the export's (or lookup's) own settings\n  - `import.settingName`: Access the import's own settings\n  - `integration.settingName`: Access integration-level settings\n  - `connection.settingName`, `iClient.settingName`: Access the step's\n    connection and iClient settings\n- A scope whose resource has no settings can be absent; a rule that reads\n  it matches nothing\n- Must be wrapped in a type conversion like `extract`: a bare\n  `[\"settings\", ...]` operand is refused with 422 \"Field operation settings\n  must be called by a Type Conversion Operation\"\n- Useful for dynamic filtering based on configuration\n\n**Field Transformations**\n- `\"lowercase\"`: Convert string to lowercase\n- `\"uppercase\"`: Convert string to uppercase\n- `\"ceiling\"`: Round number up to the nearest integer\n- `\"floor\"`: Round number down to the nearest integer\n- `\"abs\"`: Get absolute value of a number\n\n**Arithmetic** (number operands)\n- `\"add\"`, `\"subtract\"`, `\"multiply\"`, `\"divide\"`, `\"modulo\"`: for example\n  `[\"subtract\", [\"number\", [\"extract\", \"total\"]], [\"number\", [\"extract\", \"discount\"]]]`.\n  `mod` is not an operator (422 \"Invalid operation: mod.\").\n\n**Dates**\n- `epochtime` reads ISO 8601 strings and epoch-millisecond numbers. An empty\n  string, a string that isn't a date, or epoch milliseconds sent as a string\n  fail it with \"Invalid date. Cannot convert given string or number to date\n  time.\", followed by `; record=` and the record as JSON: in a run that record fails (`FILTER_EVALUATION_FAILED`, source\n  `output_filter` on an export), and `POST /v1/processors/filter` and an\n  export preview refuse the whole call with 422. `and` and `or` evaluate\n  every operand, so a `notempty` test beside it doesn't prevent the failure.\n  A missing or null field doesn't match.\n- `datetime`, `date` and `timestamp` are not type conversions: 422\n  \"Invalid operation: <name>.\"\n- `number` reads a missing or empty field as 0.\n\n**Handlebars operands**\n- An operand can also be a string holding exactly one Handlebars expression, such as\n  `\"{{record.status}}\"` or `\"{{lowercase record.code}}\"`, on either side of the\n  operator. It renders against the record (`record.<field>`; an unprefixed\n  `{{status}}` renders empty) before the comparison.\n- The rendered value is a string: wrap it in `[\"number\", ...]` to compare it with a\n  number, or the rule is refused with 422.\n- `epochtime` does not render Handlebars: `[\"epochtime\", \"{{timestamp}}\"]` is refused\n  with 422 \"invalid datetime format\".\n- A string with text around the expression, or with more than one expression, is\n  compared as a literal.\n- `[\"number\", \"{{timestamp \\\"x\\\"}}\"]` is the current time in epoch milliseconds,\n  so `[\"greaterthanequals\", [\"epochtime\", [\"extract\", \"created\"]], [\"subtract\",\n  [\"number\", \"{{timestamp \\\"x\\\"}}\"], 604800000]]` keeps records created in the\n  last 7 days.\n- An unknown helper fails the rule with 422 \"Failed to evaluate handlebars\n  expression\".\n\nChoose the type conversion that matches the comparison — `number` for numeric\ncomparisons, `epochtime` for dates, `string` for text. For example:\n\n```json\n[\n  \"equals\",\n  [\n    \"number\",  // Convert to number before comparison\n    [\n      \"extract\",\n      \"quantity\"\n    ]\n  ],\n  100\n]\n```\n\nExample with datetime conversion:\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",  // Convert to Unix timestamp before comparison\n    [\n      \"extract\",\n      \"createdDate\"\n    ]\n  ],\n  1609459200000  // January 1, 2021 as Unix timestamp in milliseconds\n]\n```\n\nExample with transformations:\n```json\n[\n  \"and\",\n  [\n    \"matches\",\n    [\n      \"lowercase\",  // Convert to lowercase before matching\n      [\n        \"string\",\n        [\n          \"extract\",\n          \"categories\"\n        ]\n      ]\n    ],\n    \"netsuite\"\n  ],\n  [\n    \"notequals\",\n    [\n      \"string\",\n      [\n        \"extract\",\n        \"recurrence.pattern.type\"\n      ]\n    ],\n    \"\"\n  ]\n]\n```\n\nExample comparing a record field with a flow setting:\n```json\n[\n  \"equals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"trantype\"\n    ]\n  ],\n  [\n    \"string\",\n    [\n      \"settings\",\n      \"flow.trantype\"\n    ]\n  ]\n]\n```\n\n**Examples**\n\nExample 1: Status field is not equal to \"cancelled\"\n```json\n[\n  \"notequals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"status\"\n    ]\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"filename\"\n    ]\n  ],\n  \"HC\"\n]\n```\n\nExample 3: Amount is greater than 100\n```json\n[\n  \"greaterthan\",\n  [\n    \"number\",\n    [\n      \"extract\",\n      \"amount\"\n    ]\n  ],\n  100\n]\n```\n\nExample 4: Order date is after January 1, 2023 (UTC). Compare dates as `epochtime`\nagainst epoch milliseconds: a `string` comparison of ISO timestamps orders values with\ndifferent UTC offsets wrongly.\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",\n    [\n      \"extract\",\n      \"orderDate\"\n    ]\n  ],\n  1672531200000\n]\n```\n","items":{"oneOf":[{"title":"String","type":"string"},{"title":"Number","type":"number"},{"title":"Boolean","type":"boolean"},{"title":"Object","type":"object"},{"title":"Array","type":"array"},{"title":"Null","type":"null"}]}}}},"script":{"type":"object","description":"Configuration for programmable script-based filtering. This object enables complex, custom\nfiltering logic beyond what expression-based filtering can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to determine which items\nshould be processed.\n\n**Implementation approach**\n\nScript-based filtering works by:\n1. Executing the specified function from the referenced script\n2. Passing item data to the function\n3. Using the function's return value (true/false) to determine inclusion\n\n**Common use cases**\n\nScript filtering is ideal for:\n- Complex business logic that can't be expressed as simple rules\n- Dynamic filtering criteria that change based on external factors\n- Content-based filtering that requires deep inspection\n- Advanced pattern matching beyond simple wildcards\n- Multi-stage filtering with intermediate logic\n\nFor AI agents: Only use script-based filtering when expression-based filtering is insufficient.\nScript filtering requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to the Script resource that contains the filtering logic. This must be a valid\nObjectId of a Script resource in this account: any other id fails the save with 422\n(\"Only a script owned by the user or a script shared by the Integration App can be used\nat filter.script._scriptId.\").\n\nThe referenced script must contain the function specified in the `function` field\nand must be written to handle filtering specifically. The script receives\nitem data as its input and must return a boolean value indicating whether\nto process the item (true) or skip it (false).\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n"},"function":{"type":"string","description":"Name of the function within the script to execute for filtering decisions. This function\nmust exist in the script referenced by _scriptId.\n\n**Function requirements**\n\nThe specified function must:\n- Accept item data as its first parameter\n- Return a boolean value (true to process the item, false to skip it)\n- Handle errors gracefully\n- Execute efficiently (as it may run for many items)\n\n**Function signature**\n\n```javascript\nfunction filterItems(itemData) {\n  // itemData contains properties of the item being evaluated\n  // Custom logic here\n  return true; // or false to skip the item\n}\n```\n\nFor AI agents: Ensure the function name exactly matches a function defined in the\nreferenced script, as mismatches will cause the filter to fail.\n"}}}}},"Distributed":{"type":"object","description":"Authentication settings for distributed (real-time listener) exports.","properties":{"bearerToken":{"type":"string","description":"Bearer token that a system pushing data into this export sends as\n`Authorization: Bearer <token>`. Optional — most distributed listeners (NetSuite,\nSalesforce real-time) use no token. Write-only and stored encrypted; treat it as a\nsensitive credential.","format":"password"}}},"Cdc":{"type":"object","description":"Change-data-capture configuration for real-time listener exports (the \"Listen for real-time\ndata\" source step). Present only when `type` is `stream`. Set on `MongodbExport` (MongoDB change\nstreams) and `RDBMSExport` (SQL Server CDC and PostgreSQL logical replication); the listener\nstreams change events continuously instead of fetching records on a schedule.\n\nOn save the API normalizes the `properties` grid into the typed fields: a `snapshot.mode`\nentry populates `snapshotMode` and a `capture.mode` entry populates `captureMode`. Every\nstream export must resolve a snapshot mode from one of those two places.","properties":{"captureMode":{"type":"string","enum":["change_streams_update_full","change_streams_update_full_with_pre_image"],"default":"change_streams_update_full","description":"How MongoDB change-stream events are materialized into records. Use\n`change_streams_update_full_with_pre_image` when downstream steps need the document's prior\nstate. MongoDB listeners only — `RDBMSExport` (SQL Server CDC, PostgreSQL) does not set this."},"snapshotMode":{"type":"string","enum":["initial","no_data","when_needed","initial_only"],"default":"no_data","description":"Whether the listener seeds existing rows before streaming changes. Use `initial` to backfill\nexisting data on the first run; `no_data` to stream only new changes."},"slotName":{"type":"string","pattern":"^[a-z_][a-z0-9_]{0,62}$","description":"Name of the PostgreSQL logical replication slot the listener consumes. PostgreSQL listeners\nonly. When omitted, the platform manages the slot name. Must be a valid PostgreSQL\nidentifier (lowercase letters, numbers, or underscores, starting with a letter or\nunderscore)."},"publicationName":{"type":"string","pattern":"^[a-z_][a-z0-9_]{0,62}$","description":"Name of the PostgreSQL publication that defines which tables stream changes. PostgreSQL\nlisteners only. When set, the publication is the sole table filter and `rdbms.tables`\nstays unset. Must be a valid PostgreSQL identifier."},"cursor":{"type":"string","description":"Server-managed stream position marker (where the listener resumes reading change events).\nMaintained by the platform as events are consumed; the UI exposes a reset workflow for it\nunder Cursor Management."},"paths":{"type":"array","items":{"type":"string"},"description":"Dot-notation paths within each raw change event to retain in the emitted record. MongoDB\nlisteners default to `[\"payload\"]` (the whole change document); SQL Server and PostgreSQL\nlisteners commonly select `payload.after`, `payload.op`, and related fields to expose the\nchanged row and operation type."},"intervalTime":{"type":"integer","default":300,"description":"Polling interval, in seconds, at which the listener checks the source for new change events."},"properties":{"type":"array","description":"Connector-engine settings passed through to the underlying CDC connector as name/value\npairs. Mirrors the form's additional-properties grid: the required keys (`capture.mode`\nand `snapshot.mode` for MongoDB, `snapshot.mode` for SQL Server and PostgreSQL) are\nserialized first, followed by optional keys such as `field.exclude.list`,\n`column.include.list`, or `snapshot.collection.filter.overrides.<db>.<collection>`.\nOn save the API also lifts `snapshot.mode` / `capture.mode` entries into the typed\n`snapshotMode` / `captureMode` fields.","items":{"type":"object","required":["name","value"],"properties":{"name":{"type":"string","description":"Connector property key (e.g. `capture.mode`, `snapshot.mode`, `field.exclude.list`)."},"value":{"type":"string","description":"Connector property value."}}}}}},"Topic":{"type":"object","description":"Subscription settings for an Event Streams listener (`adaptorType: TopicExport`). The\nlistener consumes messages published to the topic and hands them to the flow in batches;\nthe platform delivers them internally, so the export carries no `_connectionId`. Present\nwhen `adaptorType` is `TopicExport`; the server fills `startPosition` and `maxWaitTimeMs`\nwith their defaults when they are omitted.","properties":{"_topicId":{"type":"string","format":"objectId","description":"Topic this listener subscribes to. Create or look it up with `GET /v1/topics`; the\ntopic must belong to the same account. Pointing an existing listener at a different\ntopic starts a new subscription, read from `startPosition`. A clone made in the same\nenvironment — of this export, or of a flow that contains it — keeps `_topicId`, so the\ncopy becomes another listener on the topic once its flow is enabled."},"startPosition":{"type":"string","enum":["latest","earliest"],"default":"latest","description":"Where the listener starts reading when it first subscribes to `_topicId`. Use `earliest`\nto backfill retained history, `latest` (the default) to process only new messages. Read\non create and whenever `_topicId` changes; on other updates the stored value is kept even\nif a different value is sent."},"maxWaitTimeMs":{"type":"integer","minimum":1000,"default":30000,"description":"Longest time, in milliseconds, the listener waits to fill a batch before delivering\nwhatever it has collected. Lower values reduce latency for sparse topics; the default\nof 30 seconds favors fuller batches. The batch record cap is the export's `pageSize`."}}},"FileSystem":{"type":"object","description":"Defines which files to read from a local or mounted folder on the host running the\non-premise agent. Required when the _connectionId field references a file-system (on-premise)\nconnection; must not be included for other connection types.","required":["directoryPath"],"properties":{"directoryPath":{"type":"string","description":"Folder on the on-premise agent's host to read files from; the agent's OS account must\nhave read permission on it. Accepts a local OS path or a UNC network share, and supports\nhandlebars templates for dynamic folders."}}},"Http":{"type":"object","description":"Configuration for HTTP exports. Required when the export's `_connectionId` references a\nconnection of type `http`, and on `NetSuiteHTTPExport` (NetSuite's REST APIs on a `netsuite`\nconnection, where `nsDomainType` supplies the host).","properties":{"type":{"type":"string","enum":["file","blob","csvstream"],"description":"Set to `file` when the export works with files rather than a record-returning endpoint: raw file downloads (PDFs, images, binary data) and the folder mode of the cloud file providers (Google Drive, Box, Dropbox, Azure Blob, Google Cloud Storage, Celigo Storage), where each file in the folder is parsed by the `file` object's `type` (csv, json, xml, xlsx, filedefinition — including EDI) into records; the `file` object must also be configured. Leave undefined for standard data exports that return structured records (JSON, XML, GraphQL, SOAP responses) — the response is then parsed into records for downstream steps. When set to `file` or `blob`, the export appears as a \"Transfer\" step in Flow Builder instead of a standard \"Export\" step."},"formType":{"type":"string","enum":["http","rest","graph_ql","assistant","assistant_graphql"],"description":"Authoring mode of the HTTP request form. Controls which editor the UI presents\n(standard HTTP, legacy REST, GraphQL, or connector-assistant driven) and how the\nrequest fields are interpreted. When omitted, the standard HTTP form is used."},"method":{"type":"string","description":"HTTP method used to request data from the target API. Consult the target API's\ndocumentation to determine the appropriate method. `HEAD` exists for connector endpoints\nthat return metadata in response headers only (Azure Data Lake Storage Gen2 path\nproperties).","enum":["GET","POST","PUT","PATCH","DELETE","HEAD"]},"followRedirects":{"type":"boolean","default":true,"description":"When explicitly false, 3xx responses are not followed — the redirect\nresponse itself (status code, `Location` header, body) becomes the\nrecord. Omitted or true follows allowed redirects (the default\nbehavior)."},"maxRedirects":{"type":"integer","minimum":1,"maximum":10,"description":"Caps how many consecutive 3xx redirects are followed. Only applies\nwhen `followRedirects` is not false; to not follow at all, set\n`followRedirects: false` rather than `maxRedirects: 0`. Decimal\nvalues are rejected on save."},"relativeURI":{"type":"string","description":"Resource path appended to the connection's `baseURI` to form the complete request URL. Path segments, query parameters, or the entire value can be built with handlebars expressions for endpoints determined at runtime. For lookup exports (`isLookup: true`) with mappings configured, handlebars always render against the original pre-mapped input record, so fields removed or renamed by mappings remain available for URI construction. On `NetSuiteHTTPExport` the base URL is derived from the NetSuite connection and `nsDomainType`, so the path starts at `/services/rest/...` or `/app/site/hosting/restlet.nl`.\n\nLeave it empty to call the connection's `baseURI` exactly; `/` appends a trailing slash, which is a different URL. An export saved with an empty relative URI and no `http.body` comes back from reads without its `http` block, so a PUT built from such a read loses the stored settings: send the complete `http` block with every PUT."},"headers":{"type":"array","description":"Headers specific to this export, merged with (and able to override) headers defined on the connection. Define common headers such as authentication on the connection instead. Values support handlebars expressions; for lookup exports with mappings, values render against the pre-mapped input record.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header to send; a header defined here overrides a same-named header from the connection."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions; for lookup exports with mappings, the value renders against the pre-mapped input record."}}}},"requestMediaType":{"type":"string","description":"Overrides the connection-level request media type for this export. Set only when this endpoint requires a different format than the connection default.","enum":["json","xml","urlencoded","form-data","plaintext"]},"body":{"type":"string","description":"Request body sent with POST, PUT, or PATCH requests, typically carrying query or filter criteria for APIs (such as GraphQL or SOAP) that expect them in the body. The content must match the format set by `requestMediaType` and supports handlebars expressions for dynamic values."},"successMediaType":{"type":"string","description":"Media type used to parse successful response bodies. Set only when the response format differs from the request format.","enum":["json","xml","csv","plaintext"]},"errorMediaType":{"type":"string","description":"Media type used to parse error response bodies. Set only when error responses use a different format than the request.","enum":["json","xml","plaintext"]},"_asyncHelperId":{"type":"string","format":"objectId","description":"AsyncHelper resource that handles polling for long-running operations on APIs that process requests asynchronously (HTTP 202 responses, job tickets, feed or document IDs). Set when the export must submit a request, poll for status, and retrieve results once the external process completes — for example Amazon SP-API feeds or large report generators."},"once":{"type":"object","description":"Callback configuration for once exports, used to mark records as exported in the source system after successful processing.\nCalled only while the export's `type` is `once`: an export with no `type` keeps a saved `once` and never calls it, so every\nrun reads every record again (live-verified 2026-10-22).","properties":{"relativeURI":{"type":"string","description":"Relative path (starting with `/`) called on the source system to mark each record as exported after successful processing. Supports handlebars variables and renders against the pre-mapped record — mappings do not apply to the callback URI."},"method":{"type":"string","description":"HTTP method used for the mark-as-exported callback request.","enum":["GET","PUT","POST","PATCH","DELETE"]},"body":{"type":"string","description":"Request body sent with the mark-as-exported callback. Supports handlebars expressions for dynamic values."}}},"paging":{"type":"object","description":"Controls how the export requests subsequent pages when the API returns multi-page responses. The `method` field determines which companion fields are required and how each next page is requested. For the page, skip, and token methods, reference the matching pagination variable (`{{export.http.paging.page}}`, `{{export.http.paging.skip}}`, or `{{export.http.paging.token}}`) in the relative URI or request body — misconfigured pagination is a common cause of incomplete data retrieval.\n\nWhen this export is the first lookup of a builder-mode API with `pagination.enabled` set, it is that API's volume driver: saving the export (even unchanged) invalidates the API's issued cursors, so a caller mid-walk gets an empty final page, and removing `paging` is accepted on save but makes every call to the API fail with `400 api_pagination_source_not_paged` until paging is restored or the API's flag is turned off.\n\nWhen a next page's response is identical to the previous one, paging stops: the export records an open error with code `page_response_error` (\"Identical response received from next page request. No more page requests will be made.\") and the run completes with the pages already read, so later pages are never exported. A request that doesn't change between pages produces this, as observed with `method: page` and the pagination variable missing from the relative URI and body; reference the variable so each request asks for the next page.","properties":{"method":{"type":"string","description":"Pagination strategy used to request each subsequent page; match it to the mechanism documented by the target API. Determines which companion fields are required: `token` needs `path` and `pathLocation`, `url` needs `path`, `relativeuri` needs `relativeURI`, `body` needs `body`, while `linkheader` typically needs no extra configuration. Using the wrong method results in errors or incomplete data retrieval.","enum":["linkheader","page","skip","token","url","relativeuri","body"]},"page":{"type":"integer","description":"Starting page number for `method: page`. Set to 1 for APIs whose first page is not zero-indexed; when omitted, paging starts at 0. The value is incremented automatically for each subsequent page request."},"skip":{"type":"integer","description":"Starting offset for `method: skip`. Rarely needed — most APIs start at 0, and the value is incremented by the page size automatically for each subsequent request. Set only when the API requires a non-zero starting offset."},"token":{"type":"string","description":"Initial token for `method: token`. Leave empty for normal pagination — the first request is sent without a token, and subsequent tokens are extracted from each response via `path`. Set only to resume from a known token or for APIs that require a token on the first request."},"path":{"type":"string","description":"Location of the pagination value in each response — the continuation token for `method: token`, or the complete next-page URL for `method: url`. When `pathLocation` is `body`, use a dot-notation JSON path (e.g. `meta.nextToken`); when `header`, use the exact case-sensitive header name. Not used by other pagination methods."},"pathLocation":{"type":"string","description":"Where the export looks for the pagination token referenced by `path`. Required for `method: token`; not used by other pagination methods.","enum":["body","header"]},"pathAfterFirstRequest":{"type":"string","description":"Alternative token location used for responses after the first page, in the same format as `path`. Set only when the API moves the token to a different location in subsequent responses — setting it unnecessarily can cause pagination to fail."},"relativeURI":{"type":"string","description":"Overrides the main relative URI for second and later page requests; leave empty when the main relative URI works for all pages. Build it with handlebars using the `previous_page` context — `previous_page.full_response` (the prior response body), `previous_page.last_record` (the last record of the prior page), and `previous_page.headers` (the prior response headers)."},"body":{"type":"string","description":"Overrides the main request body for second and later page requests, typically for GraphQL or SOAP APIs that paginate through the body; leave empty when the main body works for all pages. Build it with handlebars using the `previous_page` context — `previous_page.full_response`, `previous_page.last_record`, and `previous_page.headers`."},"mergeBodyParamsToPagingBody":{"type":"boolean","description":"Only applies when `paging.method` is `body`. When true, the body\nparameters produced by the export's mappings at runtime are merged\ninto the evaluated `paging.body` for page 2+ requests, so paging\nrequests keep the same mapped parameters as the first page.\nOmitted/false keeps the original behavior — page 2+ requests send\nonly the evaluated `paging.body`."},"linkHeaderRelation":{"type":"string","description":"Link header relation followed for `method: linkheader` when the API uses a value other than the default `next`. Case-sensitive and must exactly match the `rel` value in the Link header, without the `rel=` prefix."},"resourcePath":{"type":"string","description":"Overrides the path to records for second and later page responses. Set only when follow-up pages place records at a different location than the first response; leave empty when all pages share the same structure."},"lastPageStatusCode":{"type":"integer","description":"HTTP status code that signals the last page, replacing the default behavior of treating 404 as the end of pagination. When this status is received (any code but `200` paired with `lastPagePath`), paging stops and the response is not treated as an error. Set it when the API uses a non-404 code (such as 204 or 400) to indicate no more pages, or to `200` together with `lastPagePath` when the last page is an ordinary 200 response: then the response is parsed, its records are kept, and paging stops only when `lastPagePath` matches `lastPageValues`."},"lastPagePath":{"type":"string","description":"JSON path to a response-body field that signals the end of pagination, such as a \"has more\" flag or a cursor that empties on the last page. Must be used with `lastPageValues`, which lists the values at this path that stop paging. If the path does not exist in a response, the condition is not considered met. With `method: token` the path is read only on a response whose status code equals `lastPageStatusCode`, so pair it with `lastPageStatusCode: 200` for an ordinary last page; with `method: body` it is read on every response."},"lastPageValues":{"type":"array","description":"Values at `lastPagePath` that stop pagination; a match on any entry ends paging. All entries are compared as exact, case-sensitive strings, even for boolean or numeric fields — use `\"true\"`, `\"false\"`, `\"null\"` for JSON null, or `\"\"` for an empty string. Required when `lastPagePath` is set.","items":{"type":"string"}},"maxPagePath":{"type":"string","description":"JSON path to the total page count in the response, used to stop paging once the last page is reached. Only applies to the `page` and `skip` methods. Point it at the total number of pages, not the current page number."},"maxCountPath":{"type":"string","description":"JSON path to the total record count in the response, used to stop paging once all records have been retrieved. Only applies to the `page` and `skip` methods; when both are set, `maxPagePath` takes precedence. Point it at the total number of records, not the count in the current page."}},"if":{"properties":{"method":{"const":"body"}},"required":["method"]},"then":{"required":["body"]},"else":{"if":{"properties":{"method":{"const":"token"}},"required":["method"]},"then":{"required":["path","pathLocation"]},"else":{"if":{"properties":{"method":{"const":"relativeuri"}},"required":["method"]},"then":{"required":["relativeURI"]},"else":{"if":{"properties":{"method":{"const":"url"}},"required":["method"]},"then":{"required":["path"]}}}}},"response":{"type":"object","description":"Controls how records are extracted from the API response and how success or failure is detected at the response level. When the API wraps records in an envelope object, set `resourcePath` to the path of the records array — without it, the entire response body is treated as a single record. Leave this object undefined when the API returns a bare JSON array.","properties":{"resourcePath":{"type":"string","description":"Dot-separated path to the array of records inside the response body (e.g. `data.customers` for `{\"data\": {\"customers\": [...]}}`); without it, a wrapped response is treated as a single record. Values starting with `$` are evaluated as JSONPath instead of dot notation — including unions (`$['orders','invoices'][*]`) and filter expressions (`$.items[?(@.qty>3)]`); all other values keep the existing dot-notation behavior unchanged. Leave undefined when the API returns a bare JSON array. This extracts records from the API response — not to be confused with `oneToMany`/`pathToMany`, which unwrap arrays from input records, or `paging.resourcePath`, which applies only to subsequent page responses."},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"},"resourceIdPath":{"type":"string","description":"Path to the unique identifier within each record, used primarily when processing results of asynchronous import responses. When omitted, the system looks for standard `id` or `_id` fields automatically."},"successPath":{"type":"string","description":"Path to a response field that signals whether the call succeeded, for APIs that return HTTP 200 even on failure. With `successValues`, the value at this path must match one of them; without `successValues`, the path only has to resolve — a response in which it is absent fails with `response_failure` (\"Unable to extract paths from response body using path: <path>, Response: …\")."},"successValues":{"type":"array","items":{"type":"string"},"description":"Values at `successPath` that mark the response as successful; any other value is treated as an error. All comparisons are string-based — use `\"true\"` or `\"false\"` for boolean fields."},"errorPath":{"type":"string","description":"Path to the error message in the response body. The value at this path is included in error logs and error records when the API returns an error; without it, the message is the whole response body."},"failPath":{"type":"string","description":"Path to a response field that signals failure even when the HTTP status code is 200 — the inverse of `successPath`. With `failValues`, the response fails when the value there matches one of them; without, when the path holds a truthy value. A path that resolves to an array matches only when the array has exactly one element."},"failValues":{"type":"array","items":{"type":"string"},"description":"Values at `failPath` that mark the response as failed, even when the HTTP status code is 200."},"allowArrayforSuccessPath":{"type":"boolean","description":"When true, treats the value at `successPath` as an array and counts the response as successful if any element matches `successValues`, rather than requiring a single scalar match. Set it for APIs that return per-record status arrays in a batch response."},"twoDArray":{"type":"object","description":"Parsing options for endpoints that return tabular data as a two-dimensional array (rows of cells) rather than an array of objects, such as spreadsheet-style or report APIs.","properties":{"hasHeader":{"type":"boolean","description":"When true, the first row is treated as column headers and used to name the fields of each generated record."},"doNotNormalize":{"type":"boolean","description":"When true, the rows are passed through as raw arrays instead of being normalized into keyed records."}}},"blobFormat":{"type":"string","description":"Controls how the binary response body is decoded for blob exports. Only relevant when `http.type` is `file` or the export type is `blob`.","enum":["utf8","ucs2","utf-16le","ascii","binary","base64","hex"]}}},"_httpConnectorVersionId":{"type":"string","format":"objectId","description":"Pins the export to a version of the published HTTP connector its connection is bound to\n(`connection.http._httpConnectorId`), so the UI renders that connector's endpoint form instead\nof a hand-authored request. Linked on save together with `_httpConnectorResourceId` and\n`_httpConnectorEndpointId` — see that field for the rule."},"_httpConnectorResourceId":{"type":"string","format":"objectId","description":"The connector resource (a group of related endpoints, such as Orders) that this export's\nendpoint belongs to. Linked on save together with `_httpConnectorEndpointId` — see that field\nfor the rule."},"sendAuthForFileDownloads":{"type":"boolean","description":"When true, includes authentication headers when downloading files."},"_httpConnectorEndpointId":{"type":"string","format":"objectId","description":"The connector endpoint this export calls, shown in the UI as the selected endpoint. On save the\nplatform derives it (with the version and resource ids) from `relativeURI` and `method` when\nthey match an endpoint of the connector the connection is bound to — path parameters, literal\nids and query strings still match — and replaces ids that belong to another connector. An id for\nthe connection's own connector is kept as sent even when the URI names a different endpoint;\nwhen nothing matches, or the connection has no connector, whatever was sent is kept and nothing\nis derived."}}},"Salesforce":{"type":"object","description":"Configuration for Salesforce exports. Required when the export's `_connectionId` references a Salesforce connection; omit for other connection types. The `type` field selects the extraction mode: `soql` runs batch queries and requires the `soql` object, while `distributed` listens for real-time events and requires the `distributed` object. File retrieval (when the export's `type` is `blob`) requires `sObjectType` and `id` instead.","properties":{"type":{"type":"string","description":"Selects the extraction mode and determines which configuration object is required. Use `soql` for scheduled or on-demand batch queries and lookups — it requires the `soql` object and works with both the `rest` and `bulk` APIs and the standard, delta, test, and once export types. Use `distributed` for real-time event handling — it requires the `distributed` object, always appears as a Listener in Flow Builder, ignores the `api` field, and supports only the standard export type.","enum":["soql","distributed"]},"sObjectType":{"type":"string","description":"API name of the Salesforce object the export operates on; the object must exist in the connected org and be accessible to the integration user. Required for distributed exports and for blob exports — blob exports accept only the file storage objects (`Attachment`, `ContentVersion`, `Document`) paired with the `id` field. Optional for SOQL exports, where the object can be inferred from the query."},"id":{"type":"string","description":"Salesforce record ID of the file to retrieve; required for blob exports and not used for `soql` or `distributed` exports. Accepts a static ID or a handlebars expression such as `{{record.Attachment_Id__c}}` to resolve the file at runtime — dynamic IDs require the export to run as a lookup (`isLookup: true`). The ID must belong to the object named in `sObjectType`."},"includeDeletedRecords":{"type":"boolean","description":"When true, SOQL exports use Salesforce's `queryAll()` instead of `query()`, including Recycle Bin records deleted within the past 15 days; each record's `IsDeleted` field identifies deletions. Useful for synchronizing deletes to target systems or maintaining a complete audit trail. Ignored for distributed and blob exports.","default":false},"api":{"type":"string","description":"Salesforce API used to run SOQL queries; ignored for distributed and blob exports. Use `rest` (the default when omitted) for smaller datasets (under 10,000 records) and for lookup exports — the Bulk API is not compatible with `isLookup: true`. Use `bulk` for large data volumes and higher throughput, optionally tuned through the `bulk` object.","enum":["rest","bulk"]},"bulk":{"type":"object","description":"Settings for Salesforce Bulk API 2.0 jobs. Only applies when `api` is `bulk` on a SOQL export; ignored otherwise.","properties":{"maxRecords":{"type":"integer","description":"Caps how many records a single Bulk API job retrieves, which helps prevent timeouts with complex queries or large records. When omitted, Salesforce's default applies. Lower values suit complex or custom objects; higher values improve throughput for simple records.","minimum":10000},"purgeJobAfterExport":{"type":"boolean","default":true,"description":"When true, deletes the Bulk API job in Salesforce after all data is retrieved, keeping the Bulk Data Load Jobs list clean — at the cost of making job details unavailable for later troubleshooting. Has no effect on the data retrieved or the success of the export."}}},"soql":{"type":"object","description":"SOQL query configuration; required when `type` is `soql` and not used otherwise. The query controls which objects, fields, and filter conditions the export retrieves, with either the REST or Bulk API.\n\nRequired when type is soql.","properties":{"query":{"type":"string","description":"SOQL statement passed directly to Salesforce that defines which objects, fields, and records the export retrieves. Supports handlebars — for delta exports, filter with `WHERE LastModifiedDate > {{lastExportDateTime}}` — and relationship queries that fetch parent and child records together. Select only the fields you need and use `ORDER BY` for consistent results across pages.","maxLength":200000}}},"distributed":{"type":"object","description":"Real-time listener configuration — the object that makes an export a listener. Set it, with `type: distributed`, when creating one; a listener saved from the integrator.io form carries it with no `type`, and the platform fires it either way. Not used on SOQL or file exports. Records reach the flow only when an Apex trigger on `sObjectType` in the connected org calls the Celigo package (`integrator_da__.RealTimeExporter.processExport()`) — no scheduling or manual execution is involved. Integration Apps create their triggers at installation; for a custom flow the customer deploys the trigger in Salesforce, starting from the code the export page shows under Required trigger.","properties":{"referencedFields":{"type":["array","null"],"description":"Fields from related objects to include in the exported record, written in dot notation (e.g. `Account.Name`, `Owner.Email`, `Custom_Lookup__r.Field__c`). Works with lookup and master-detail relationships, up to 10 unique referenced relationships per export. Referenced fields are retrieved via separate API calls, so include only fields the integration actually needs.","items":{"type":["string","null"]}},"disabled":{"type":"boolean","description":"When true, the listener stops processing events while its configuration and Salesforce triggers stay in place. Events that occur while disabled are ignored, not queued — they are not processed retroactively when re-enabled, so consider a delta export to catch up after extended pauses."},"qualifier":{"type":["string","null"],"description":"SOQL WHERE-clause condition that filters which record events the listener processes; when omitted (stored as `null`), every event for the object is processed. The Celigo Salesforce package evaluates it inside Salesforce as `SELECT Id FROM <sObjectType> WHERE (<qualifier>) AND Id IN :ids` before anything is sent, so only fields of the monitored object and SOQL comparison syntax apply — Salesforce formula functions are not part of this grammar. A deleted record is never returned by that query, so a listener with any qualifier receives no delete events; leave it unset when removals must trigger the flow."},"batchSize":{"type":["integer","null"],"description":"Controls how many event records are grouped into each real-time processing batch. Stored as `null` when not configured. Smaller batches lower latency for time-sensitive operations; larger batches improve throughput for high-volume objects. Does not limit how many records are processed in total — only how they are grouped.","minimum":4,"maximum":200},"skipExportFieldId":{"type":["string","null"],"description":"API name of a Salesforce checkbox field used to prevent infinite loops in bidirectional syncs: when the integration updates a record, this field is set so the resulting event is ignored, then cleared automatically. Required for bidirectional sync scenarios. The field must be a checkbox dedicated to integration use and updateable by the integration user."},"relatedLists":{"type":["array","null"],"description":"Child record sets to include with the parent record when it changes, one entry per related list. When omitted (stored as `null`), only the primary record is processed. Each related list adds Salesforce API calls and increases payload size.","items":{"type":"object","description":"Defines how to retrieve one type of child record related to the primary object. Configure multiple entries to retrieve different types of children.","properties":{"referencedFields":{"type":"array","description":"API names of the child object fields to retrieve; only the listed fields are included, and an empty array retrieves only the `Id` field. Include only fields the integration needs — each field adds data volume and processing time.","items":{"type":["string","null"]}},"parentField":{"type":"string","description":"API name of the lookup or master-detail field on the child object that references the parent (e.g. `AccountId`, `Parent_Object__c`) — the field's API name, not the relationship name. Used to build the query that fetches children for each parent record."},"sObjectType":{"type":"string","description":"Case-sensitive API name of the child object to retrieve (e.g. `Contact`, `Custom_Child__c`) — use the API name, not the label. The object must have a relationship field to the parent and be accessible to the connected user."},"filter":{"type":"string","description":"SOQL condition that limits which child records are included; when omitted, all related children are retrieved. Provide only the condition expression without the `WHERE` keyword — it is combined automatically with the parent relationship filter."},"orderBy":{"type":"string","description":"Sort order for the retrieved child records, as field names with optional `ASC`/`DESC` directions (e.g. `CreatedDate DESC`). Provide only the fields and directions without the `ORDER BY` keywords. When omitted, Salesforce determines the order."}},"required":["parentField","referencedFields","sObjectType"]}}}}},"if":{"properties":{"type":{"const":"distributed"}},"required":["type"]},"then":{"required":["distributed","sObjectType"]},"else":{"if":{"required":["distributed"]},"then":{},"else":{"if":{"required":["id"]},"then":{"required":["sObjectType"]},"else":{"if":{"not":{"propertyNames":{"enum":["metadata"]}}},"then":{"required":["soql","type"]}}}}},"AS2":{"type":"object","description":"Configures AS2 exports and listeners. Optional — the transport settings live on the AS2\nconnection, and an AS2 export is valid with only `file` (parse settings); set this object\nto link a Trading Partner Connector or to transfer raw files as blobs. An AS2 listener\nacts as the flow's source, receiving trading-partner transmissions in near real-time and\nhandling decryption, signature verification, and MDN generation; AS2 (Applicability\nStatement 2) transmits EDI and other data securely over HTTP/S using S/MIME encryption\nand digital signatures.","properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies the partner-specific EDI configuration —\ncommunication protocol, document schemas, mappings, validation rules, and endpoint\ndetails. Set this to link the export to all settings required for AS2 communication\nwith that partner."},"blob":{"type":"boolean","description":"When true, retrieves raw files without parsing them into structured records (rendered as\na \"Transfer\" step in the flow UI). Use only when the file contents are not needed in\nsubsequent steps — for binary files or when parsing is handled downstream. Only\navailable on AS2 and VAN exports."}},"required":[]},"DynamoDB":{"type":"object","description":"Defines how records are queried from DynamoDB tables. Required when the _connectionId field\nreferences a DynamoDB connection; must not be included for other connection types. Basic\nexports need region, method, tableName, keyConditionExpression, expressionAttributeNames,\nand expressionAttributeValues; once exports (export type \"once\") additionally need\nonceExportPartitionKey, plus onceExportSortKey for composite-key tables.","required":["region","tableName","keyConditionExpression","expressionAttributeNames","expressionAttributeValues"],"properties":{"region":{"type":"string","enum":["us-east-1","us-east-2","us-west-1","us-west-2","af-south-1","ap-east-1","ap-south-1","ap-northeast-1","ap-northeast-2","ap-northeast-3","ap-southeast-1","ap-southeast-2","ca-central-1","eu-central-1","eu-west-1","eu-west-2","eu-west-3","eu-south-1","eu-north-1","me-south-1","sa-east-1"],"description":"AWS region hosting the DynamoDB table. Must match the region where the table is\ndeployed so the integration can reach it.","default":"us-east-1"},"method":{"type":"string","enum":["query"],"description":"DynamoDB operation used to retrieve items. Only \"query\" is currently supported."},"tableName":{"type":"string","description":"Name of the DynamoDB table to query. Must exactly match an existing table (case-sensitive)."},"keyConditionExpression":{"type":"string","description":"Key condition determining which items the query retrieves. Must include a condition on\nthe partition key and may add sort-key conditions (equality, BETWEEN, begins_with).\nReference attribute names with \"#\" placeholders defined in expressionAttributeNames and\nvalues with \":\" placeholders defined in expressionAttributeValues."},"filterExpression":{"type":"string","description":"Filters query results on non-key attributes after the key condition is applied; omit to\nreturn all items matching the key condition. Uses the same \"#\" and \":\" placeholders\ndefined in expressionAttributeNames and expressionAttributeValues."},"projectionExpression":{"type":"array","items":{"type":"string"},"description":"Attributes to return from each item, reducing data transfer; omit to return all\nattributes. Each array element is one field, referenced via \"#\" placeholders defined in\nexpressionAttributeNames."},"expressionAttributeNames":{"type":"string","description":"JSON string mapping \"#\" placeholders to actual attribute names, e.g.\n{\"#pk\": \"customerId\"}. Placeholders defined here are used in keyConditionExpression,\nfilterExpression, and projectionExpression."},"expressionAttributeValues":{"type":"string","description":"JSON string mapping \":\" placeholders to comparison values, e.g. {\":status\": \"ACTIVE\"}.\nValues can be static or dynamic handlebars expressions such as {{lastExportDateTime}},\nand are referenced from keyConditionExpression and filterExpression."},"onceExportPartitionKey":{"type":"string","description":"Partition key attribute that uniquely identifies items when the export's type is \"once\".\nCeligo uses it to mark items as processed after a successful export, preventing the same\nitems from being exported again on subsequent runs."},"onceExportSortKey":{"type":"string","description":"Sort key attribute used together with onceExportPartitionKey to identify processed items\nwhen the table has a composite primary key. Omit for tables keyed by a partition key\nalone."},"pathToRecords":{"$ref":"#/components/schemas/PathToRecords"},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"}}},"PathToRecords":{"type":"string","maxLength":250,"description":"Path to the array of records inside each document or item fetched from the source; each array element is emitted as an individual record, which keeps records within the 5 MB page-size limit even when the source document exceeds it. Accepts dot notation (e.g. `data.orders`) or JSONPath for values starting with `$`, including unions (`$['orders','invoices'][*]`) and filter expressions (`$.items[?(@.qty>3)]`); a path that resolves to nothing emits zero records for that document or item. When omitted, each document or item is emitted as one record, exactly as before — HTTP and file-based exports configure this same capability through their existing `http.response.resourcePath` and `file.json`/`file.xml` `resourcePath` fields."},"FTP":{"type":"object","description":"Defines which files to retrieve from an FTP, FTPS, or SFTP server. Required when the\n_connectionId field references an FTP/SFTP connection; must not be included for other\nconnection types. directoryPath selects the folder, fileNameStartsWith/fileNameEndsWith\nfilter files by name, and backupDirectoryPath controls where files are moved after\nretrieval.","required":["directoryPath"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies partner-specific B2B settings for this export.\nWhen set, the export inherits the connector's pre-configured settings; omit to use only\nthe FTP connection details."},"directoryPath":{"type":"string","description":"Directory on the server to retrieve files from, either absolute or relative to the login\ndirectory; the FTP user must have read permission on it. Use forward slashes regardless\nof server OS — paths are case-sensitive on UNIX/Linux servers. Supports handlebars\ntemplates, e.g. archive/{{date 'YYYY-MM-DD'}}."},"fileNameStartsWith":{"type":"string","description":"Only retrieves files whose names start with this value (case-sensitive on most servers);\naccepts static text or handlebars templates. When combined with fileNameEndsWith, files\nmust match both."},"fileNameEndsWith":{"type":"string","description":"Only retrieves files whose names end with this value, commonly a file extension\n(case-sensitive on most servers). When combined with fileNameStartsWith, files must\nmatch both."},"backupDirectoryPath":{"type":"string","description":"Directory on the same server where files are moved after successful export, giving you\nan independent backup; if omitted, files are simply deleted from the source directory\nafter successful export unless `file.skipDelete` is true (Celigo also keeps its own copy\nof processed files for a set period). Supports static paths or handlebars templates."}}},"JDBC":{"type":"object","description":"Configuration object for JDBC (Java Database Connectivity) data integration exports.\n\nThis object is REQUIRED when the _connectionId field references a JDBC database connection\nand must not be included for other connection types. It defines how data is extracted\nfrom relational databases using SQL queries.\n\n**Jdbc export capabilities**\n- Execute custom SQL SELECT statements\n- Support for joins, aggregations, and functions\n- Flexible data retrieval from any accessible tables or views\n- Compatible with all major database systems\n**Critical:** WHAT BELONGS IN THIS OBJECT\n- `query` - **ALWAYS REQUIRED** - The SQL SELECT statement\n- `once` - **REQUIRED** when the export's Object Type is `\"once\"` (set _include_once: true)\n- **DO NOT** put `delta` inside this object - delta is handled via the query\n\n**Delta exports (type: \"delta\")**\nFor delta/incremental exports, do NOT populate a `delta` object inside `jdbc`.\nInstead, use `{{lastExportDateTime}}` or `{{currentExportDateTime}}` directly in the query:\n```json\n{\n  \"type\": \"delta\",\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE updatedAt > {{lastExportDateTime}}\"\n  }\n}\n```\n\n**Once exports (type: \"once\")**\nFor once exports (mark records as processed), populate `jdbc.once.query`:\n```json\n{\n  \"type\": \"once\",\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE exported = false\",\n    \"once\": {\n      \"query\": \"UPDATE customers SET exported = true WHERE id = {{record.id}}\"\n    }\n  }\n}\n```\n\n**Standard exports (type: null or not specified)**\nJust provide the query:\n```json\n{\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE status = 'ACTIVE'\"\n  }\n}\n```","required":["query"],"properties":{"formType":{"type":["string","null"],"enum":["sql","simple",null],"description":"Selects how the export's query is authored. Use `sql` for a hand-written SQL statement in\n`query`; use `simple` to build the query from the `simple` object's table/columns/filter."},"query":{"type":"string","description":"SQL SELECT statement executed to retrieve data, from simple table selections to joins and\naggregations. For delta exports, include {{lastExportDateTime}}/{{currentExportDateTime}}\nin the WHERE clause rather than configuring a separate delta object."},"once":{"type":"object","description":"**CRITICAL: REQUIRED when the export's Object Type is \"once\".**\n\nIf Object Type is \"once\", you MUST set _include_once to true (or include this object).\n\nThis object has ONLY ONE property: \"query\" (a SQL UPDATE string).\nDO NOT create any other properties like \"update\", \"table\", \"set\", \"where\", etc.\n\nCORRECT format:\n```json\n{\"query\": \"UPDATE customers SET exported=true WHERE id={{record.id}}\"}\n```\n\nWRONG format (DO NOT DO THIS):\n```json\n{\"update\": {\"table\": \"customers\", \"set\": {...}}}\n```\n","properties":{"query":{"type":"string","description":"**REQUIRED** - A SQL UPDATE statement string to mark records as processed.\n\nThis is a plain SQL UPDATE query string, NOT a structured object.\n\nThe query runs AFTER each record is successfully exported, setting a flag\nto indicate the record has been processed.\n\nFormat: \"UPDATE <table> SET <column>=<value> WHERE <id_column>={{record.<id_field>}}\"\n\nExample: \"UPDATE customers SET exported=true WHERE id={{record.id}}\"\n\nThe {{record.id}} placeholder is replaced with the actual record ID from each exported row.\n"}}},"simple":{"type":"object","description":"Visual query-builder configuration used when `formType` is `simple`; the platform builds\nthe SELECT from these instead of raw SQL. Only relevant when `formType` is `simple`.","properties":{"table":{"type":"string","description":"Table or view to select rows from."},"columns":{"type":"array","items":{"type":"string"},"description":"Columns to return; an empty list selects all columns."},"filter":{"description":"Filter applied as the WHERE clause for the generated query, stored as the\nplatform's structured filter-rules object.","allOf":[{"$ref":"#/components/schemas/Filter"}]}}}}},"MongoDB":{"type":"object","description":"Defines how documents are retrieved from MongoDB collections. Required, with `method` and\n`collection`, when the _connectionId field references a MongoDB connection and the export\nfetches documents; must not be included for other connection types. A change-stream\nlistener (`type: stream`) is configured through `cdc` instead and may omit this object, or\nset only `collection` to scope the listener. Supports find queries with optional filter\ncriteria and field projections.","properties":{"method":{"type":"string","enum":["find"],"description":"MongoDB operation used to retrieve documents. Only \"find\" is currently supported,\nequivalent to db.collection.find(filter, projection)."},"collection":{"type":"string","description":"Name of the MongoDB collection to query. Case-sensitive and must reference an existing\ncollection in the connected database. Required on find exports; on a change-stream\nlistener (`type: stream`) it is optional and may list several collections separated by\ncommas, matching the listener form's \"Collections\" field."},"filter":{"type":"string","description":"MongoDB query document, as a JSON string, that selects which documents to export; omit\nto return every document in the collection. Supports standard MongoDB query operators\nand handlebars variables for dynamic values — for example\n{\"lastModified\": {\"$gt\": \"{{lastRun}}\"}} for incremental processing."},"projection":{"type":"string","description":"MongoDB projection document, as a JSON string, that limits which fields are returned;\nomit to return all fields. Use 1 to include fields or 0 to exclude them — the two modes\ncannot be mixed except for _id, which is included by default unless explicitly excluded.\nProjection affects only the shape of returned documents, not which documents match."},"pipeline":{"type":"string","description":"MongoDB aggregation pipeline, as a JSON-array string of stages, used instead of a plain\nfind when documents need to be transformed, joined, or grouped server-side (e.g. `$match`,\n`$lookup`, `$group`). When set, it takes the place of `filter`/`projection`. Supports\nhandlebars variables for incremental processing."},"readPreference":{"type":"string","enum":["doNotOverride","primary","primaryPreferred","secondary","secondaryPreferred","nearest"],"default":"doNotOverride","description":"Overrides which replica-set member this export reads from, trading consistency for load\ndistribution. Leave at `doNotOverride` to inherit the connection's setting."},"pathToRecords":{"$ref":"#/components/schemas/PathToRecords"},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"}}},"NetSuite":{"type":"object","description":"NetSuite-specific export configuration. Required when `_connectionId` references a NetSuite\nconnection; omit for all other connection types. Supports saved-search, RESTlet, and\ndistributed (SuiteApp) exports, plus two file cabinet modes: blob exports transfer files\nas-is (set the export's top-level `type` to `blob` and configure `netsuite.blob`), while\nfile exports parse file contents into records (leave the export's top-level `type` unset\nand configure `netsuite.file`).","properties":{"type":{"type":"string","enum":["search","basicSearch","metadata","selectoption","restlet","getList","getServerTime","distributed","file"],"description":"Controls how data is retrieved from NetSuite and which sibling object must be configured:\n`search` pairs with `netsuite.searches`, `restlet` with `netsuite.restlet`, `distributed`\nwith `netsuite.distributed`, and `file` with `netsuite.file.folderInternalId`. For blob\nexports (raw file transfer without parsing), leave this field unset, set the export's\ntop-level `type` to `blob`, and configure `netsuite.internalId` instead. For lookups\n(`isLookup: true`), prefer `restlet`, which supports `suiteapp2.0` saved searches with\ndynamic inputs; `search` is limited for dynamic lookups."},"searches":{"type":"array","description":"Saved search configurations that query NetSuite for records. Each entry references a\nsaved search by internal ID, names the record type being searched, and can add filter\ncriteria.","items":{"type":"object","properties":{"savedSearchId":{"type":"string","description":"Internal ID of the NetSuite saved search to run."},"recordType":{"type":"string","description":"NetSuite record type being searched, as its exact lowercase script ID\n(e.g. \"customer\", \"salesorder\") — not the display name."},"criteria":{"type":"array","description":"Additional filter criteria applied on top of the saved search.","items":{"type":"object","properties":{"field":{"type":"string","description":"Script-id of the search column to filter on.  REQUIRED.\nMust come from the record type's SEARCH COLUMNS list\n(NOT the body-fields list).  Becomes the `name`\nargument of NetSuite's `search.createFilter()`."},"operator":{"type":"string","description":"Comparison operator.  REQUIRED.  Supported values\ndepend on the column's data type (e.g. `is`,\n`isnot`, `after`, `before`, `anyof`,\n`noneof`, `contains`, `startswith`,\n`greaterthan`, `lessthan`).  Must be a string\nNetSuite recognises -- mismatched operators cause\nruntime rejection."},"join":{"type":"string","description":"Optional join name when filtering through a related\nrecord (e.g. `customer` from a sales order).  Use\nonly when the column lives on a related record and\nNetSuite supports the join from the base record type."},"searchValue":{"description":"Value (or array of values for `anyof`-style\noperators) to compare against the column.  Required\nfor most operators; omit for the few unary operators\n(e.g. `isempty`, `isnotempty`)."}},"required":["field","operator"]}}}}},"metadata":{"type":"object","description":"Supplemental metadata associated with the NetSuite export."},"selectoption":{"type":"object","description":"Configuration for `selectoption` exports, which retrieve the available select options\nfor a NetSuite field."},"customFieldMetadata":{"type":"object","description":"Metadata describing the custom fields defined in the NetSuite account."},"skipGrouping":{"type":"boolean","description":"When true, each saved-search result row becomes its own record. When false or omitted,\nconsecutive result rows that share the record's internal id are grouped into one record\nshaped as an array of rows — the *Group rows* option in the UI."},"statsOnly":{"type":"boolean","description":"When true, returns only summary statistics about matching records instead of the\ndetailed records themselves."},"internalId":{"type":"string","description":"Internal ID of the file in the NetSuite file cabinet to export. Required for blob\nexports (the export's top-level `type` is `blob`); the file is transferred as-is\nwithout parsing. For parsed file exports, use `netsuite.file.folderInternalId` instead."},"blob":{"type":"object","properties":{"purgeFileAfterExport":{"type":"boolean","description":"When true, permanently deletes the file from the NetSuite file cabinet after a\nsuccessful export. When false or omitted, the file is left in place."}},"description":"Configuration for blob exports, which transfer files from the NetSuite file cabinet\nas-is without parsing them into records. To export a blob, set the export's top-level\n`type` to `blob`, set `netsuite.internalId` to the file's internal ID, and leave\n`netsuite.type` unset. Use `netsuite.file` instead when file contents should be parsed\ninto records."},"restlet":{"type":"object","properties":{"recordType":{"type":"string","description":"NetSuite record type the RESTlet operates on, as its script ID. Custom record\ntypes use the \"customrecord_\" prefix."},"batchSize":{"type":"number","description":"Number of records the RESTlet returns per request (default 1000). This, not the\nexport's `pageSize`, sizes each NetSuite call; the export keeps requesting until the\nsearch is exhausted. Larger batches reduce round-trips but raise NetSuite governance\nand memory use per call; tune to the record's size. The RESTlet caps a call at 1000,\nso a larger value acts as 1000. In a test run or preview the request is capped at the\nexport's `pageSize` (default 20) instead."},"searchId":{"type":["string","null"],"description":"Internal ID of the NetSuite saved search the RESTlet executes. Results follow the\nsaved search's current definition, so editing the search in NetSuite changes the\nexport output without changing this ID."},"useSS2Restlets":{"type":"boolean","description":"When true, calls SuiteScript 2.0 RESTlets. When false or omitted, legacy\nSuiteScript 1.0 RESTlets are used.\nSet at step creation together with restletVersion — the same\ncreation-time-only constraint applies (see restletVersion)."},"restletVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"RESTlet version the export invokes. Defaults to `suiteapp2.0` when\n`useSS2Restlets` is true, `suitebundle` otherwise.\nThe version is fixed when the step is created — the Advanced selector is\ndisabled on existing steps, and migrating an existing export to a\ndifferent version means recreating or cloning it."},"criteria":{"type":"array","description":"Filter conditions added on top of the saved search (the UI's \"Additional search\ncriteria\"). Each condition pairs a search column with an operator and one or two\ncomparison values, and the conditions are combined with AND — every one must match.","items":{"type":"object","properties":{"field":{"type":"string","description":"Script-id of the search column to filter on. This is the primary\nfield the criterion is evaluated against (e.g. \"trandate\", \"status\")."},"join":{"type":"string","description":"Join relationship that applies this filter to a related record's fields instead\nof the base record. Must match a join name NetSuite defines for the record type."},"operator":{"type":"string","enum":["after","allof","any","anyof","before","between","contains","doesnotcontain","doesnotstartwith","equalto","greaterthan","greaterthanorequalto","haskeywords","is","isempty","isnot","isnotempty","lessthan","lessthanorequalto","noneof","notafter","notallof","notbefore","notbetween","notequalto","notgreaterthan","notlessthan","notlessthanorequalto","noton","notonorafter","notonorbefore","notwithin","on","onorafter","onorbefore","startswith","within"],"description":"Comparison operator applied between the column and the search value, using\nNetSuite's lowercase search operator names. Which operators a column accepts\ndepends on its data type."},"searchValue":{"description":"Value the column is compared against — a string, a number, or an array of\nvalues for `anyof`-style operators. On lookup steps it may be a Handlebars\nexpression rendered from the incoming record (for example `{{poNumber}}`)."},"searchValue2":{"description":"Second comparison value for the two-value operators `between` and `notbetween`\n(the upper bound). Omit for every other operator."},"formula":{"type":"string","description":"NetSuite formula expression used as the filter condition, for criteria that\nstandard field-operator-value comparisons can't express."}}}},"columns":{"type":"array","description":"Search columns the RESTlet returns for each matching record. With a saved search,\nthe RESTlet returns the search's own result columns followed by any listed here: a\nlist adds columns and never removes one of the search's, so remove a column in the\nsaved search itself. An ad-hoc search (no `searchId`) must list its columns here.","items":{"type":"object","properties":{"_id":{"type":"string","readOnly":true,"description":"Subdocument id the server assigns to the column entry on save. Not a NetSuite\nfield id — never set it when authoring."},"name":{"type":"string","description":"Column field name as referenced in the search."},"join":{"type":"string","description":"Join relationship that sources this column from a related record instead of\nthe base record. Supports dot notation for nested joins (e.g. \"employee.manager\")."},"summary":{"type":"string","enum":["group","sum","count","avg","min","max"],"description":"Summary type applied to this column, turning the search into a summary search."},"formula":{"type":"string","description":"NetSuite formula expression that computes this column's value at runtime, such\nas a CASE statement or date formatting, instead of reading a stored field."},"label":{"type":"string","description":"Display name for the column; it becomes the property name of the column's value\nin each exported record."},"sort":{"type":"boolean","description":"When true, query results are sorted by this column's values."}}}},"markExportedBatchSize":{"type":"integer","description":"Number of records updated per RESTlet call when marking records as exported in\nNetSuite (live: an integer count, e.g. 100).","maximum":100,"minimum":1},"hooks":{"type":["object","null"],"properties":{"batchSize":{"type":"number","description":"Legacy location of the RESTlet page size; `restlet.batchSize` is the current\nfield and takes precedence when both are set."},"preSend":{"type":"object","properties":{"fileInternalId":{"type":"string","description":"Internal ID of the JavaScript file in the NetSuite File Cabinet that defines\nthe hook function. Must be readable by the role behind the export's connection."},"function":{"type":"string","description":"Name of the global function in that file that NetSuite invokes as the pre\nsend hook."},"configuration":{"type":"object","description":"Free-form JSON stored with the hook reference for the script's own use. Custom\nsettings in scope for the export are passed to the function separately as\n`options.settings`."}},"description":"SuiteScript hook that runs inside NetSuite after a page of records is collected\nand before it is sent to the Celigo platform."}},"description":"SuiteScript hooks for the export. Only `preSend` exists on exports; imports carry\nthe pre map, post map, and post submit hooks."},"cLocked":{"type":"object","description":"Lock state that prevents modification of the configuration when set."}},"description":"Configuration for `restlet` exports. Identifies the RESTlet script that retrieves data\nfrom NetSuite, along with the saved search, criteria, and columns it executes with."},"distributed":{"type":"object","properties":{"recordType":{"type":"string","description":"NetSuite record type the listener is deployed on, as its lowercase script ID\n(`customer`, `salesorder`, `customrecord_<scriptid>`) — never the display name or a\ncustom record's numeric internal ID. Types the platform's record-type catalog marks\n`doesNotSupportRTExport` cannot host a listener and are not offered in the UI."},"executionContext":{"type":"array","description":"NetSuite execution contexts that trigger this distributed export. A record change\nfires the export only when it occurs in one of the listed contexts.","default":["userinterface","webstore"],"items":{"type":"string","enum":["userinterface","webservices","csvimport","offlineclient","portlet","scheduled","suitelet","custommassupdate","workflow","webstore","userevent","mapreduce","restlet","webapplication","restwebservices"]}},"disabled":{"type":"boolean","description":"When true, the listener's NetSuite deployment is inactive and record saves no longer\ntrigger the export. Maintained by the platform as the flow is disabled and re-enabled;\nre-enabling the flow re-registers the deployment. A listener is created disabled, and\na flow created already enabled leaves it that way until a flow update sets\n`disabled: false`, which activates it within seconds."},"executionType":{"type":"array","description":"Record operations that trigger this distributed export. A record event fires the\nexport only when its operation matches one of the listed types.","default":["create","edit","xedit"],"items":{"type":"string","enum":["create","edit","delete","xedit","copy","view","cancel","approve","reject","pack","ship","markcomplete","reassign","editforecast","dropship","specialorder","orderitems","paybills","print","email"]}},"qualifier":{"type":["array","string","null"],"description":"Field-specific qualification criteria the SuiteApp evaluates against the record before\nexporting it; saves that do not match are discarded. Stored as a filter-expression array\nof `[field, operator, value]` rules joined by lowercase `\"and\"` / `\"or\"`, as a legacy\nexpression string, or null when no criteria are set."},"skipExportFieldId":{"type":"string","description":"Script ID of a checkbox body field on the record type. A save with that box checked is\nskipped by the listener, which then clears the box itself — used to keep a mass update\nfrom fanning out and to stop a write-back import from re-triggering the listener."},"hooks":{"type":"object","description":"SuiteScript hook that runs inside NetSuite before the record leaves it.","properties":{"preSend":{"type":"object","description":"Pre-send hook: a SuiteScript function in the NetSuite file cabinet invoked before\nrecords leave NetSuite, with the full SuiteScript API available — for example to run\ndynamic searches against the record being exported.","properties":{"fileInternalId":{"type":"string","description":"Internal ID of the file cabinet file that holds the hook's SuiteScript."},"function":{"type":"string","description":"Name of the function in that file to invoke for each export."},"configuration":{"type":"object","description":"Settings passed to the hook function."}}}}},"sublists":{"type":["array","null"],"description":"IDs of the NetSuite sublists to include with each exported record (for example `item`\nfor transaction line items). Records are exported with body fields only when no\nsublists are selected. Legacy documents may store null (equivalent to no sublists).","items":{"type":"string"}},"referencedFields":{"type":"object","description":"Field references recorded on the listener by the platform. Not exposed in the listener\nform; leave as stored."},"relatedLists":{"type":"object","description":"Related record lists recorded on the listener by the platform. Not exposed in the\nlistener form; leave as stored."},"forceReload":{"type":"boolean","description":"When true, the listener reloads the record from the database after the save before\nexporting it, so fields that are populated only once the save completes — such as\ntransaction line IDs on new lines — are included. The reload slows every save of that\nrecord type."},"ioEnvironment":{"type":"string","description":"integrator.io environment the listener's NetSuite deployment reports to. Written by the\nplatform when the listener registers with NetSuite; live-observed values include\n`production` and `staging`."},"ioDomain":{"type":"string","description":"integrator.io domain the listener's NetSuite deployment calls back to — the account's\nown regional domain, stamped by the platform on every save and echoed on create and\nupdate responses; not returned by GET."},"lastSyncedDate":{"type":"string","format":"date-time","description":"When the listener's deployment last synchronized with NetSuite. Written by the platform;\nnot set until the first successful synchronization."},"settings":{"type":"object","description":"Additional key-value settings for the distributed export. Not exposed in the listener\nform."},"useSS2Framework":{"type":"boolean","description":"Derived by the platform from `frameworkVersion` — true when it is `suiteapp2.0`, absent\notherwise — and echoed on create and update responses; not returned by GET. A value\nsent on create or update is replaced by the derived one."},"frameworkVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"SuiteApp framework version used by the distributed export.\nFixed when the step is created — the Advanced selector is disabled on\nexisting steps, and migrating an existing listener to a different\nversion means recreating or cloning it."}},"description":"Configuration for `distributed` exports, which use the NetSuite SuiteApp to fire\nreal-time, event-driven exports when records change. Define the record type to listen\nto and the execution contexts and operations that trigger it."},"getList":{"type":"array","description":"Configuration for `getList` exports — a list of record references, each retrieving one\nNetSuite record by its identifier. The export returns the fetched records in list order.","items":{"type":"object","properties":{"type":{"type":"string","description":"Standard NetSuite record type to retrieve (e.g. \"customer\", \"salesOrder\").\nUsed when `typeId` is absent. Case-sensitive."},"typeId":{"type":"string","description":"Script ID of a custom record type (\"customrecord_...\") or custom transaction\ntype (\"customtransaction_...\"). Takes precedence over `type` when present.\nCase-sensitive."},"internalId":{"type":"string","description":"Internal ID of the NetSuite record to retrieve. Assigned by NetSuite at record\ncreation and unique within the record type. Takes precedence over `externalId`."},"externalId":{"type":"string","description":"Identifier assigned by an external system, used to reference and synchronize the\nrecord across systems. Distinct from the NetSuite-assigned internal ID."}}}},"searchPreferences":{"type":"object","properties":{"bodyFieldsOnly":{"type":"boolean","description":"When true, search results include only the record's body fields and exclude fields\nfrom joined or related records. Reduces payload size when related data isn't needed."},"pageSize":{"type":"number","description":"Number of search results NetSuite returns per page."},"returnSearchColumns":{"type":"boolean","description":"When true, search results include the column data defined in the search. When\nfalse, column data is omitted and results contain only minimal record information."}},"description":"Preferences that control how NetSuite executes searches and shapes the results it returns."},"file":{"type":"object","description":"Configuration for file exports, which retrieve files from the NetSuite file cabinet\nand parse their contents (CSV, XML, JSON) into records. Leave the export's top-level\n`type` unset when using this object; for raw transfers without parsing, use\n`netsuite.blob` with the export's `type` set to `blob` instead.","properties":{"folderInternalId":{"type":"string","description":"Internal ID of the NetSuite file cabinet folder to export files from. Accepts a\nhandlebars expression (e.g. `{{record.folderId}}`) when the folder must be\nselected dynamically from record data. The ID is stable across folder renames and\nmoves, but may differ between non-production and production accounts."},"backupFolderInternalId":{"type":"string","description":"Internal ID of the NetSuite file cabinet folder where backup files are stored.\nIDs are account-specific and do not transfer between non-production and production\nenvironments."},"fileNameStartsWith":{"type":"string","description":"Optional prefix filter. Only files whose names start with this string are\nexported from the configured folder."},"fileNameEndsWith":{"type":"string","description":"Optional suffix filter. Only files whose names end with this string (e.g. a\n\".csv\" extension) are exported from the configured folder."}}}},"required":[]},"RDBMS":{"type":"object","description":"Configuration object for Relational Database Management System (RDBMS) data integration exports.\n\nThis object defines how data is read from a relational database and must not be included\nfor other connection types. For query-type exports (standard, delta, once) it is REQUIRED\nwhen the _connectionId field references an RDBMS database connection and holds the SQL\nquery. For real-time stream listeners (`type: \"stream\"`) it holds the CDC watch list\n(`tables`) instead — and a PostgreSQL listener scoped by a publication\n(`cdc.publicationName`) may omit this object entirely.\n\n**Rdbms export capabilities**\n- Execute custom SQL SELECT statements\n- Support for joins, aggregations, and functions\n- Flexible data retrieval from any accessible tables or views\n- Compatible with all major database systems\n\n**Critical:** WHAT BELONGS IN THIS OBJECT\n- `query` - The SQL SELECT statement - required for every query-type export (standard, delta, once)\n- `once` - **REQUIRED** when the export's Object Type is `\"once\"` (set _include_once: true)\n- `tables` - the CDC listener watch list - real-time stream listeners (`type: \"stream\"`) only\n- **DO NOT** put `delta` inside this object - delta is handled via the query\n- **DO NOT** author `query` on a stream listener - real-time CDC exports have no SQL query\n\n**Delta exports (type: \"delta\")**\nFor delta/incremental exports, do NOT populate a `delta` object inside `rdbms`.\nInstead, use `{{lastExportDateTime}}` or `{{currentExportDateTime}}` directly in the query, and\nset the export's top-level `delta.startDate`: without it the first run's window is empty.\n```json\n{\n  \"type\": \"delta\",\n  \"delta\": { \"startDate\": \"2026-01-01T00:00:00.000Z\" },\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE updatedAt > {{lastExportDateTime}}\"\n  }\n}\n```\n\n**Once exports (type: \"once\")**\nFor once exports (mark records as processed), populate `rdbms.once.query`:\n```json\n{\n  \"type\": \"once\",\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE exported = false\",\n    \"once\": {\n      \"query\": \"UPDATE customers SET exported = true WHERE id = {{record.id}}\"\n    }\n  }\n}\n```\n\n**Standard exports (type: null or not specified)**\nJust provide the query:\n```json\n{\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE status = 'ACTIVE'\"\n  }\n}\n```\n\n**Real-time CDC listener exports (type: \"stream\")**\nSQL Server and PostgreSQL connections support change-data-capture listeners that stream row\nchanges continuously (the `cdc` object holds the listener settings). A stream export has NO\n`query` — it watches `tables`:\n```json\n{\n  \"type\": \"stream\",\n  \"rdbms\": { \"tables\": \"public.orders,public.customers\" },\n  \"cdc\": { \"properties\": [{ \"name\": \"snapshot.mode\", \"value\": \"no_data\" }] }\n}\n```\nA PostgreSQL listener can instead scope tables through a publication\n(`cdc.publicationName`), in which case `tables` stays unset and the `rdbms` object may be\nomitted entirely.","properties":{"tables":{"type":"string","description":"Comma-separated list of tables the CDC listener watches for changes. Used only by real-time\nlistener exports (`type` is `stream`); standard SQL exports use `query` instead. SQL Server\nlisteners use fully-qualified three-part names (`database.schema.table`); PostgreSQL\nlisteners use two-part names (`schema.table`)."},"query":{"type":"string","description":"SQL SELECT statement executed to retrieve data, from simple table selections to joins,\naggregations, and parameterized queries with Handlebars expressions. Query-type exports\nonly — real-time stream listeners (`type` is `stream`) have no SQL query. For delta exports,\nreference {{lastExportDateTime}}/{{currentExportDateTime}} directly in the query rather\nthan configuring a separate delta object.\n\nDouble braces render a string as a quoted SQL literal with its apostrophes escaped, and\ntriple braces insert a value raw. Observed in Snowflake queries:\n\n- String helper output (`dateFormat`, `jsonSerialize`) and both delta variables are\n  quoted the same way; a number or boolean renders bare, whether it is a JSON value or a\n  numeric helper's result (`{{add 2 3}}` renders `5`).\n- In a run, {{lastExportDateTime}} and {{currentExportDateTime}} arrive as quoted ISO 8601\n  UTC literals with milliseconds (`'2026-01-15T00:00:00.000Z'`), or in `delta.dateFormat`\n  when it is set: add no quotes of your own, and don't parse them with a format mask\n  written for another format.\n- A preview of an unsaved export document renders both as empty, unquoted text, so a\n  delta query fails to compile there unless the document carries both in `postData`.\n- The quoting leaves backslashes, which Snowflake reads as escape sequences, so a\n  double-brace value holding a backslash arrives changed. Pass such a value in a\n  dollar-quoted constant with triple braces (`$${{{record.field}}}$$`), which Snowflake\n  reads as is, but only when it can't contain `$$`: a `$$` ends the constant, and the rest\n  is read as SQL."},"once":{"type":"object","description":"**CRITICAL: REQUIRED when the export's Object Type is \"once\".**\n\nIf Object Type is \"once\", you MUST set _include_once to true (or include this object).\n\nThis object has ONLY ONE property: \"query\" (a SQL UPDATE string).\nDO NOT create any other properties like \"update\", \"table\", \"set\", \"where\", etc.\n\nCORRECT format:\n```json\n{\"query\": \"UPDATE customers SET exported=true WHERE id={{record.id}}\"}\n```\n\nWRONG format (DO NOT DO THIS):\n```json\n{\"update\": {\"table\": \"customers\", \"set\": {...}}}\n```\n","properties":{"query":{"type":"string","description":"**REQUIRED** - A SQL UPDATE statement string to mark records as processed.\n\nThis is a plain SQL UPDATE query string, NOT a structured object.\n\nThe query runs AFTER each record is successfully exported, setting a flag\nto indicate the record has been processed.\n\nFormat: \"UPDATE <table> SET <column>=<value> WHERE <id_column>={{record.<id_field>}}\"\n\nExample: \"UPDATE customers SET exported=true WHERE id={{record.id}}\"\n\nThe {{record.id}} placeholder is replaced with the actual record ID from each exported row.\n"}}}}},"S3":{"type":"object","description":"Defines which files to retrieve from an Amazon S3 bucket. Required when the _connectionId\nfield references an AWS S3 connection; must not be included for other connection types.\nregion and bucket locate the source, keyStartsWith/keyEndsWith filter objects by key, and\nbackupBucket with keyPrefix controls where files are moved after retrieval.","required":["region","bucket"],"properties":{"region":{"type":"string","default":"us-east-1","description":"AWS region where the bucket is located. Case-insensitive; the value is normalized to\nlowercase."},"bucket":{"type":"string","description":"S3 bucket to retrieve files from. The connection's AWS credentials must have\ns3:ListBucket and s3:GetObject permissions on it."},"keyStartsWith":{"type":"string","description":"Only retrieves objects whose keys start with this value (case-sensitive), effectively\nselecting a folder in S3's flat key structure. When combined with keyEndsWith, objects\nmust match both."},"keyEndsWith":{"type":"string","description":"Only retrieves objects whose keys end with this value (case-sensitive), commonly a file\nextension. When combined with keyStartsWith, objects must match both."},"backupBucket":{"type":"string","description":"Bucket in the same region where files are moved after successful export, giving you an\nindependent backup; if omitted, files are simply deleted from the source bucket after\nsuccessful export unless `file.skipDelete` is true (Celigo also keeps its own copy of\nprocessed files for a set period). The connection's AWS credentials must have\ns3:PutObject permission on this bucket."},"keyPrefix":{"type":"string","description":"Prefix prepended to each file's name when it is moved to the backup bucket; accepts\nstatic text or handlebars templates. The original directory structure is not preserved —\nonly the filename is appended to this prefix. Ignored unless backupBucket is set."}}},"Wrapper":{"type":"object","description":"Configuration for Wrapper exports, which delegate data retrieval to custom connector code\n(typically a stack-hosted function) rather than a built-in adaptor. Required when the\n_connectionId field references a wrapper connection.","required":["function"],"properties":{"function":{"type":"string","description":"Name of the function the wrapper invokes to retrieve records. Must match a callable\nfunction in the wrapper's execution context; names are case-sensitive."},"configuration":{"oneOf":[{"type":"object","additionalProperties":true},{"type":"array","maxItems":0}],"description":"Free-form settings passed to the wrapper function at runtime (connector-specific keys such\nas method, apiVersion, headers, relativePath, or body). Structure is defined by the wrapper\ncode, not by this schema. May be stored as an empty array when no settings exist."}}},"Parsers":{"type":"array","description":"Configuration for parsing XML payloads (i.e. files, HTTP responses, etc.). Use this field when you need to process XML data\nand transform it into a JSON records.\n\n**Implementation notes**\n\n- This is where you configure how to parse XML data in your resource\n- Although defined as an array, you typically only need a single parser configuration\n- Currently only XML parsing is supported\n- Only configure this field when working with XML data that needs structured parsing\n","items":{"type":"object","properties":{"version":{"type":"string","description":"Version identifier for the parser configuration format. Currently only version \"1\" is supported.\n\nAlways set this field to \"1\" as it's the only supported version at this time.\n","enum":["1"]},"type":{"type":"string","description":"Defines the type of parser to use. Currently only \"xml\" is supported.\n\nWhile the system is designed to potentially support multiple parser types in the future,\nat this time only XML parsing is implemented, so this field must be set to \"xml\".\n","enum":["xml"]},"name":{"type":"string","description":"Optional identifier for the parser configuration. This field is primarily for documentation\npurposes and is not functionally used by the system.\n\nThis field can be omitted in most cases as it's not required for parser functionality.\n"},"rules":{"type":"object","description":"Configuration rules that determine how XML data is parsed and converted to JSON.\nThese settings control the structure and format of the resulting JSON records.\n\n**Parsing options**\n\nThere are two main parsing strategies available:\n- **Automatic parsing**: Simple but produces more complex output\n- **Custom parsing**: More control over the resulting JSON structure\n","properties":{"V0_json":{"type":"boolean","description":"Controls the XML parsing strategy.\n\n- When set to **true** (Automatic): XML data is automatically converted to JSON without\n  additional configuration. This is simpler to set up but typically produces more complex\n  and deeply nested JSON that may be harder to work with.\n\n- When set to **false** (Custom): Gives you more control over how the XML is converted to JSON.\n  This requires additional configuration (like listNodes) but produces cleaner, more\n  predictable JSON output.\n\nMost implementations use the Custom approach (false) for better control over the output format.\n"},"listNodes":{"type":"array","description":"Paths of elements that always parse as arrays. An element that can repeat parses as an\nobject when it occurs once and as an array when it occurs more than once, so list every\nrepeating element here to give each record the same shape regardless of item count.\nEach entry is the element's full path from the record element selected by the resource\npath (from the document root when there is no resource path), with an optional leading\nslash. Only relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"includeNodes":{"type":"array","description":"Limits which XML nodes are included in the output JSON.\n\nFor large XML documents, you can use this field to extract only the nodes you need,\nreducing the size and complexity of the resulting JSON. Only nodes specified here\n(and their children) will be included in the output.\n\nEach entry should be a simplified XPath expression pointing to nodes to include.\n\nOnly relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"excludeNodes":{"type":"array","description":"Specifies which XML nodes should be excluded from the output JSON.\n\nSometimes it's easier to specify which nodes to exclude rather than which to include.\nUse this field to identify nodes that should be omitted from the output JSON.\n\nEach entry should be a simplified XPath expression pointing to nodes to exclude.\n\nOnly relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"stripNewLineChars":{"type":"boolean","description":"Controls whether newline characters are removed from text values.\n\nWhen set to true, all newline characters (\\n, \\r, etc.) will be removed from\ntext content in the XML before conversion to JSON.\n","default":false},"trimSpaces":{"type":"boolean","description":"Controls whether leading and trailing whitespace is trimmed from text values.\n\nWhen set to true, all values will have leading and trailing whitespace removed\nbefore conversion to JSON.\n","default":false},"attributePrefix":{"type":"string","description":"Specifies a character sequence to prepend to XML attribute names when converted to JSON properties.\n\nIn XML, both elements and attributes can exist at the same level, but in JSON this distinction is lost.\nTo maintain the distinction between element data and attribute data in the resulting JSON, this prefix\nis added to attribute names during conversion.\n\nFor example, with attributePrefix set to \"Att-\" and an XML element like:\n```xml\n<product id=\"123\">Laptop</product>\n```\n\nThe resulting JSON would be:\n```json\n{\n  \"product\": \"Laptop\",\n  \"Att-id\": \"123\"\n}\n```\n\nThis helps maintain the distinction between element content and attribute values in the\nconverted JSON, making it easier to reference specific data in downstream processing steps.\n"},"textNodeName":{"type":"string","description":"Specifies the property name to use for element text content when an element has both\ntext content and child elements or attributes.\n\nWhen an XML element contains both text content and other nested elements or attributes,\nthis field determines what property name will hold the text content in the resulting JSON.\n\nFor example, with textNodeName set to \"value\" and an XML element like:\n```xml\n<item id=\"123\">\n  Laptop\n  <category>Electronics</category>\n</item>\n```\n\nThe resulting JSON would be:\n```json\n{\n  \"item\": {\n    \"value\": \"Laptop\",\n    \"category\": \"Electronics\",\n    \"id\": \"123\"\n  }\n}\n```\n\nThis allows for unambiguous parsing of complex XML structures that mix text content with\nchild elements. Choose a name that's unlikely to conflict with actual element names in your XML.\n"}}}}}},"Mappings":{"type":"array","description":"Array of field mapping configurations for transforming data from one format into another.\n\n**Guidance**\n\nThis schema is designed around RECURSION as its core architectural principle. Understanding this recursive\nnature is essential for building effective mappings:\n\n1. The schema is self-referential by design - a mapping can contain nested mappings of the same structure\n2. Complex data structures (nested objects and arrays of objects, to any depth) are ALL\n   handled through this recursive pattern; an array of arrays is not (build one in a script hook)\n3. Each mapping handles one level of the data structure; deeper levels are handled by nested mappings\n\nWhen generating mappings programmatically:\n- For simple fields (string, number, boolean): Create single mapping objects\n- For objects: Create a parent mapping with nested 'mappings' array containing child field mappings\n- For arrays: Use 'buildArrayHelper' with extract paths defining array inputs and\n  recursive 'mappings' to define object structures\n\nThe system will process these nested structures recursively during runtime, ensuring proper construction\nof complex hierarchical data while maintaining excellent performance.\n\nOn save, a leaf row with neither a source (`extract`, `hardCodedValue`, `lookupName`) nor a\ndestination (`generate`) is dropped silently, at every nesting level, and so is an object or\nobject-array row left with no source, no destination and no remaining child `mappings` or\n`buildArrayHelper` entries; a row with only one of the two, and a source-less, destination-less\nwrapper that still has children, are kept.\n\nThe shape of the array is the mapper's **JSON structure**. Named top-level items produce an object\nrecord (`record { }`). Exactly one top-level item with `dataType: objectarray` and no `generate` is\nthe `rows [ ]` structure: the mapped record is an array — usually one element per element of the\nsource array that root's `buildArrayHelper` iterates — Handlebars templates such as `http.body`\nbind it as `rows` instead of `record`, and the mapping editor shows that row's destination disabled.\n","items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}}},"items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}},"Transform":{"type":"object","description":"Configuration for transforming data during processing operations. This object enables\nreshaping of records.\n\n**Transformation capabilities**\n\nCeligo's transformation engine offers powerful features for data manipulation:\n- Precise field mapping with JSONPath expressions\n- Support for any level of nested arrays\n- Formula-based field value generation\n- Dynamic references to flow and integration settings\n\n**Implementation approaches**\n\nThere are two distinct transformation mechanisms available:\n\n**Rule-Based Transformation (`type: \"expression\"`)**\n- **Best For**: Most transformation scenarios from simple to complex\n- **Capabilities**: Field mapping, formula calculations, lookups, nested data handling\n- **Advantages**: Visual configuration, no coding required, intuitive interface\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear mapping requirements or need to reshape data structure\n\n**Script-Based Transformation (`type: \"script\"`)**\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Capabilities**: Full programmatic control, custom processing, complex business rules\n- **Advantages**: Maximum flexibility, can implement any transformation logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Visual transformation tools aren't sufficient for your use case\n","properties":{"type":{"type":"string","description":"Determines which transformation mechanism to use. This choice affects which properties\nmust be configured and how transformation logic is implemented.\n\n**Available types**\n\n**Rule-Based Transformation (`\"expression\"`)**\n- **Required Config**: The `expression` object with mapping definitions\n- **Behavior**: Applies declarative rules to reshape data\n- **Best For**: Most transformation scenarios from simple to complex\n- **Advantages**: Visual configuration, no coding required\n\n**Script-Based Transformation (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to transform data\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard data transformations, use `\"expression\"`\n2. For complex logic or specialized processing, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based transformations. This object enables reshaping data\nwithout requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define transformation rules that\ncan map, modify, and generate data elements.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Field mappings define how input data is transformed to target fields\n- Formulas can be used to calculate or generate new values\n- Lookups can enrich data by fetching related information\n- Mode determines how records are processed (create new or modify existing)\n","properties":{"version":{"type":"string","description":"Version of the expression format. Determines which rules\nproperty contains the transformation logic.\n","enum":["1","2"]},"rules":{"type":"array","description":"Transformation rules for version 1 expressions. An array of\nrule groups; each group is an array of field-mapping objects.\nMost transforms have a single group. Present when `version`\nis `\"1\"`. The output record contains ONLY the generated\nfields — every unmapped field is dropped (v1 has no\nequivalent of Transform 2.0's `modify` mode), and the\nrecord's trace key does not survive the rebuild.\n","items":{"type":"array","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path to read from. Supports multiple\nsyntaxes: bare field names (`id`), dot notation\n(`fulfillment.shipment_id`), slash-prefixed paths\nfor XML (`/FeedProcessingStatus`), wildcards (`*.id`,\n`*.[Internal ID]`), and array indexing (`SDF[0]`).\n"},"generate":{"type":"string","description":"Target field name to write to. Typically a bare name\n(`id`) or dot path (`SDF.Filter.ID`).\n"},"key":{"type":"string","description":"Auto-generated identifier for this rule, used by the\nUI to track individual rules for editing and reordering.\n"}},"required":["extract","generate"]}}},"rulesTwoDotZero":{"type":"object","description":"Configuration for version 2 transformation rules. This object contains the core logic\nfor how data is mapped, enriched, and transformed.\n\n**Capabilities**\n\nTransformation 2.0 provides:\n- Precise field mapping with JSONPath expressions\n- Support for deeply nested data structures\n- Formula-based field generation\n- Dynamic lookups for data enrichment\n- Multiple operating modes to fit different scenarios\n","properties":{"mode":{"type":"string","description":"Transformation mode that determines how records are handled during processing.\n\n**Available modes**\n\n**Create Mode (`\"create\"`)**\n- **Behavior**: Builds entirely new output records from inputs\n- **Use When**: Output structure differs significantly from input\n- **Advantage**: Clean slate approach, no field inheritance\n\n**Modify Mode (`\"modify\"`)**\n- **Behavior**: Makes targeted edits to existing records\n- **Use When**: Output structure should remain similar to input\n- **Advantage**: Preserves unmapped fields from the original record\n","enum":["create","modify"]},"mappings":{"$ref":"#/components/schemas/Mappings"},"lookups":{"allOf":[{"description":"Shared lookup tables used across all mappings defined in the transformation rules.\n\n**Purpose**\n\nLookups provide centralized value translation that can be referenced from any mapping\nin your transformation configuration. They enable consistent translation of codes, IDs,\nand values between systems without duplicating translation logic.\n\n**Usage in transformations**\n\nLookups are particularly valuable in transformations for:\n\n- **Data Normalization**: Standardizing values from diverse source systems\n- **Code Translation**: Converting between different coding systems (e.g., status codes)\n- **Field Enrichment**: Adding descriptive values based on ID or code lookups\n- **Cross-Reference Resolution**: Mapping identifiers between integrated systems\n\n**Implementation**\n\nLookups are defined once in this array and referenced by name in mappings:\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"statusMapping\",\n    \"map\": {\n      \"A\": \"Active\",\n      \"I\": \"Inactive\",\n      \"P\": \"Pending\"\n    },\n    \"default\": \"Unknown Status\"\n  }\n]\n```\n\nThen referenced in mappings using the lookupName property:\n\n```json\n{\n  \"generate\": \"status\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.statusCode\",\n  \"lookupName\": \"statusMapping\"\n}\n```\n\nThe system automatically applies the lookup during transformation processing.\n\nFor complete details on lookup properties and behavior, see the Lookups schema.\n"},{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the JSON shape the transformTwoDotZero processor\nevaluates `mappings[].extract` JSONPath values against at\nflow runtime. Applies only to Transform 2.0 (v2,\n`rulesTwoDotZero`); v1 transforms (the `rules` array on\n`transform.expression.rules`) and script-mode transforms\nignore this field.\n"}}}}},"script":{"type":"object","description":"Configuration for programmable script-based transformations. This object enables complex, custom\ntransformation logic beyond what expression-based transformations can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to transform data according to\nspecialized business rules or complex algorithms.\n\n**Implementation approach**\n\nScript-based transformation works by:\n1. Executing the specified function from the referenced script\n2. Passing input data to the function\n3. Using the function's return value as the transformed output\n\n**Common use cases**\n\nScript transformation is ideal for:\n- Complex business logic that can't be expressed through mappings\n- Algorithmic transformations requiring computation\n- Dynamic transformations based on external factors\n- Legacy system data format compatibility\n- Multi-stage processing with intermediate steps\n\nOnly use script-based transformation when expression-based transformation is insufficient.\nScript transformation requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to a predefined script resource containing the transformation logic.\n\nThe referenced script should contain the function specified in the\n'function' property. It must be a script in this account: any other id fails the save\nwith 422 (\"Only a script owned by the user or a script shared by the Integration App can\nbe used at transform.script._scriptId.\").\n","format":"objectid"},"function":{"type":"string","description":"Name of the function within the script to execute for transformation. This function\nmust exist in the script referenced by _scriptId.\n"}}}}},"Lookups":{"type":"array","description":"Configuration for value-to-value transformations using lookup tables.\n\n**Purpose**\n\nLookups provide a way to translate values from one system to another. They transform\ninput values into output values using either static mapping tables or\ndynamic lookup caches.\n\n**Lookup mechanisms**\n\nThere are two distinct lookup mechanisms available:\n\n1. **Static Lookups**: Define a simple key-value map object and store it as part of your resource\n   - Best for: Small, fixed sets of values that rarely change\n   - Implementation: Configure the `map` object with input-to-output value mappings\n   - Example: Country codes, status values, simple translations\n\n2. **Dynamic Lookups**: Reference an existing 'Lookup Cache' resource in your Celigo account\n   - Best for: Large datasets, frequently changing values, or complex reference data\n   - Implementation: Configure `_lookupCacheId` to reference cached data maintained independently\n   - Example: Product catalogs, customer databases, pricing information\n\n**Property usage**\n\nThere are two mutually exclusive ways to configure lookups, depending on which mechanism you choose:\n\n1. **For Static Mappings**: Configure the `map` property with a direct key-value object\n   ```json\n   \"map\": {\"US\": \"United States\", \"CA\": \"Canada\"}\n   ```\n\n2. **For Dynamic Lookups**: Configure the following properties:\n   - `_lookupCacheId`: Reference to the lookup cache resource\n   - `extract`: JSON path to extract specific value from the returned lookup object\n\n**When to use**\n\nLookups are ideal for:\n\n1. **Value Translation**: Mapping codes or IDs to human-readable values\n\n2. **Data Enrichment**: Adding related information to records during processing\n\n3. **Normalization**: Ensuring consistent formatting of values across systems\n\n**Implementation details**\n\nLookups can be referenced in:\n\n1. **Field Mappings**: Direct use in field transformation configurations\n\n2. **Handlebars Templates**: Use within templates with the syntax:\n   ```\n   {{lookup 'lookupName' record.fieldName}}\n   ```\n\n**Example usage**\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"countryCodeToName\",\n    \"map\": {\n      \"US\": \"United States\",\n      \"CA\": \"Canada\",\n      \"UK\": \"United Kingdom\"\n    },\n    \"default\": \"Unknown Country\",\n    \"allowFailures\": true\n  },\n  {\n    \"name\": \"productDetails\",\n    \"_lookupCacheId\": \"60a2c4e6f321d800129a1a3c\",\n    \"extract\": \"$.details.price\",\n    \"allowFailures\": false\n  }\n]\n```\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for the lookup table within this configuration.\n\nThis name must be unique within the scope where the lookup is defined and is used to reference\nthe lookup in handlebars templates with the syntax {{lookup 'name' value}}.\n\nChoose descriptive names that indicate the transformation purpose, such as:\n- \"countryCodeToName\" for country code to full name conversion\n- \"statusMapping\" for status code translations\n- \"departmentCodes\" for department code to name mapping\n"},"map":{"type":["object","null"],"description":"The lookup mapping table as key-value pairs. The platform stores `null`\nhere on dynamic lookups, which resolve values at runtime instead of\nfrom a static table.\n\nThis object contains the input values as keys and their corresponding\noutput values. When a input value matches a key in this object,\nit will be replaced with the corresponding value.\n\nThe map should be kept to a reasonable size (typically under 100 entries)\nfor optimal performance. For larger mapping requirements, consider using\ndynamic lookups instead.\n\nMaps can include:\n- Simple code to name conversions: {\"US\": \"United States\"}\n- Status transformations: {\"A\": \"Active\", \"I\": \"Inactive\"}\n- ID to name mappings: {\"100\": \"Marketing\", \"200\": \"Sales\"}\n\nValues can be strings, numbers, or booleans, but all are stored as strings\nin the configuration.\n"},"_lookupCacheId":{"type":"string","description":"Reference to a LookupCache resource that contains the reference data for the lookup.\n\n**Purpose**\n\nThis field connects the lookup to an external data source that has been cached in the system.\nUnlike static lookups that use the `map` property, dynamic lookups can reference large datasets\nor frequently changing information without requiring constant updates to the integration.\n\n**Implementation details**\n\nThe LookupCache resource referenced by this ID contains:\n- The data records to be used as a reference source\n- Configuration for how the data should be indexed and accessed\n- Caching parameters to balance performance with data freshness\n\n**Usage patterns**\n\nCommonly used to reference:\n- Product catalogs or SKU databases\n- Customer or account information\n- Pricing tables or discount rules\n- Complex business logic lookup tables\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n","format":"objectid"},"extract":{"type":"string","description":"JSON path expression that extracts a specific value from the cached lookup object.\n\n**Purpose**\n\nWhen using dynamic lookups with a LookupCache, this JSON path identifies which field to extract\nfrom the cached object after it has been retrieved using the lookup key.\n\n**Implementation details**\n\n- Must use JSON path syntax (similar to mapping extract fields)\n- Operates on the cached object returned by the lookup operation\n- Examples:\n  - \"$.name\" - Extract the name field from the top level\n  - \"$.details.price\" - Extract a nested price field\n  - \"$.attributes[0].value\" - Extract a value from the first element of an array\n\n**Usage scenario**\n\nWhen a lookup cache contains complex objects:\n```json\n// Cache entry for key \"PROD-123\":\n{\n  \"id\": \"PROD-123\",\n  \"name\": \"Premium Widget\",\n  \"details\": {\n    \"price\": 99.99,\n    \"currency\": \"USD\",\n    \"inStock\": true\n  }\n}\n```\n\nSetting extract to \"$.details.price\" would return 99.99 as the lookup result.\n\nIf no extract is provided, the entire cached object is returned as the lookup result.\n"},"default":{"type":["string","null"],"description":"Default value to use when the source value is not found in the lookup map.\nThe platform stores `null` here when no default is configured.\n\nThis value is used as a fallback when:\n1. The source value doesn't match any key in the map\n2. allowFailures is true or omitted\n\nSetting an appropriate default helps prevent flow failures due to unexpected\nvalues and provides predictable behavior for edge cases.\n\nCommon default patterns include:\n- Descriptive unknowns: \"Unknown Country\", \"Unspecified Status\"\n- Original value indicators: \"{Original Value}\", \"No mapping found\"\n- Neutral values: \"Other\", \"N/A\", \"Miscellaneous\"\n\nA save refuses a default together with an explicit `allowFailures: false`\n(422 `Lookups with default set can not have allowFailures explicitly set to\nfalse.`): a lookup either fails a miss (`allowFailures: false`, no default) or\nfalls back to its default.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, missing lookup values will use the default value rather than causing an error.\n\n**Behavior control**\n\nThis field determines how the system handles source values that don't exist in the map:\n\n- true: Use the default value for missing mappings and continue processing\n- false: Treat missing mappings as errors, failing the record (`lookup_error` for a\n  static map, `lookupcache_evaluation_failed` for a lookup cache)\n- omitted or null: the record doesn't fail either. On an import a static lookup writes\n  its default if it has one, and a static or cache lookup without one leaves the\n  mapped field out of the output, with no error\n\n**Recommendation**\n\nSet this to true when:\n- New source values might appear over time\n- Data quality issues could introduce unexpected values\n- Processing should continue even with imperfect mapping\n\nSet this to false when:\n- Complete data accuracy is critical\n- All possible source values are known and controlled\n- Missing mappings indicate serious data problems that should be addressed\n\nA default raises no error: records that miss go out with the default value and\nnothing reports them. Use true only when that fallback is acceptable at the\ndestination, and set false explicitly when a missing value must stop the record.\n"}}}},"Form":{"type":"object","description":"Configuration for creating user-friendly settings forms that make it easier for less technical users\nto configure integration resources.\n\n**Settings form builder**\n\nThe Settings Form Builder allows you to create or edit user-friendly fields that prompt for text entry\nor selections that will be returned as settings applied to this resource. Your forms can include any\nfield types that you see elsewhere in integrator.io, such as:\n\n- Text fields\n- Dropdown selections\n- Checkboxes\n- Radio buttons\n- Date pickers\n- Multi-select fields\n- Search fields\n\nForm fields make it much easier for less technical users to work with your integration settings by:\n\n- Providing clear labels and help text\n- Enforcing validation rules\n- Offering pre-defined selection options\n- Grouping related settings logically\n- Supporting conditional visibility\n- Creating a consistent user experience\n","properties":{"form":{"type":"object","description":"Configuration that defines the structure, fields, and behavior of the settings form.\n\nThis object contains the complete definition of the form's layout, fields, validation rules,\nand interactive behaviors. The specific structure depends on the form complexity and can include\nfield definitions, sections, conditional display logic, and default values.\n\nThe form configuration is typically created and managed through the visual Form Builder interface\nrather than edited directly as JSON.\n","properties":{"fieldMap":{"type":"object","description":"A mapping of field identifiers to their configuration objects.\nEach key in this object represents a unique field ID, and the value contains\nall the configuration settings for that specific form field.\n","additionalProperties":{"type":"object","description":"Configuration for an individual form field.\n","properties":{"id":{"type":"string","description":"Unique identifier for this field within the form: layout containers and conditions\nrefer to the field by it. Use the same string as the field's fieldMap key and its `name`.\n"},"name":{"type":"string","description":"Key the field's value is saved under: one top-level key of the resource's `settings`,\nwhich the form also reads the value back from when it opens. Nested objects inside\n`settings` are written to it directly, not through a form field's `name`.\n"},"type":{"type":"string","description":"The type of form control to render for this field.\n","enum":["text","checkbox","radiogroup","relativeuri","editor","keyvalue","select","multiselect","toggle","datetime","date","exportSelect","staticMap"]},"label":{"type":"string","description":"Display label shown next to the field in the form.\n"},"description":{"type":"string","description":"Detailed explanation text that appears below the field, providing more context\nthan the label or helpText.\n"},"helpText":{"type":"string","description":"Explanatory text that appears when hovering over the help icon next to the field.\nUsed to provide additional guidance on how to use the field.\n"},"required":{"type":"boolean","description":"When true, the field must have a value before the form can be submitted. Only the form\nenforces it: an API write of `settings` is stored without the value.\n","default":false},"defaultValue":{"description":"Value the form shows while the resource's `settings` holds no value under the field's\n`name`.\n"},"multiline":{"type":"boolean","description":"For text fields, determines whether the input should be a multi-line text area\ninstead of a single-line input.\n","default":false},"rowsMax":{"type":"integer","description":"For multiline text fields, specifies the maximum number of visible rows.\n"},"inputType":{"type":"string","description":"For text fields, specifies the HTML input type attribute to apply additional\nvalidation or specialized input behavior.\n","enum":["text","number","email","password","tel","url"]},"delimiter":{"type":"string","description":"For text fields, specifies a character to use for splitting the input into an array.\nUsed for collecting multiple values in a single text field.\n"},"mode":{"type":"string","description":"For editor fields, specifies the type of content being edited for syntax highlighting.\n","enum":["json","xml","csv","text"]},"keyName":{"type":"string","description":"For keyvalue and staticMap fields, names the first column: each saved row holds that\ncolumn's value under this key. A keyvalue field without it uses `key`.\n"},"valueName":{"type":"string","description":"For keyvalue and staticMap fields, names the second column: each saved row holds that\ncolumn's value under this key. A keyvalue field without it uses `value`.\n"},"keyLabel":{"type":"string","description":"For staticMap fields, heading of the first column.\n"},"valueLabel":{"type":"string","description":"For staticMap fields, heading of the second column.\n"},"keyOptions":{"type":"array","description":"For staticMap fields, suggestions offered in the first column's cells. A cell still\naccepts a typed value.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"Display text for the suggestion.\n"},"value":{"type":"string","description":"Value saved when the suggestion is chosen.\n"}}}},"valueOptions":{"type":"array","description":"For staticMap fields, suggestions offered in the second column's cells. A cell still\naccepts a typed value.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"Display text for the suggestion.\n"},"value":{"type":"string","description":"Value saved when the suggestion is chosen.\n"}}}},"keyResource":{"type":"object","description":"For staticMap fields, fills the first column's suggestions from an export the form\ncarries inline in `virtual`, run the way an exportSelect's `resource` is. With\n`keyResource` or `valueResource` set, the form requires both columns in every row\nunless `keyValueRequired` is false.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline, shaped like an exportSelect's `resource.virtual`.\n","additionalProperties":true}}},"valueResource":{"type":"object","description":"For staticMap fields, fills the second column's suggestions from an export the form\ncarries inline in `virtual`, run the way an exportSelect's `resource` is.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline, shaped like an exportSelect's `resource.virtual`.\n","additionalProperties":true}}},"keyValueRequired":{"type":"boolean","description":"For staticMap fields with `keyResource` or `valueResource`, when false, saves rows\nthat fill only one column; by default the form drops them.\n","default":true},"resource":{"type":"object","description":"For exportSelect fields, the export whose records become the options. When the form\nopens, the UI previews the export in `virtual` (with `test.limit` 100 unless the export\nsets its own) and lists the preview's `data`: each record needs a `label` and a `value`\n(a string or number serves as both), so shape them with the export's `transform`. A\nsaved export can't be referenced, and an HTTP export that finds no records leaves the\nfield showing an error instead of options.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline: `adaptorType`, its adaptor block, and an optional\n`transform`. `_connectionId` defaults to the connection of the export or import the\nform belongs to; set it on any other resource's form.\n","additionalProperties":true}}},"multiselect":{"type":"boolean","description":"For exportSelect fields, when true, the user can choose several options and the value\nis saved as an array.\n","default":false},"supportHandlebars":{"type":"boolean","description":"For exportSelect fields, when true, renders the `resource.virtual` export as a Handlebars\ntemplate before each preview (all but its `transform`, `hooks`, and `filter`). On an\nintegration, flow group, flow, export, or import form, the form's current values are\nunder that form's scope name (`integration`, `flowGrouping`, `flow`, `export`, or\n`import`), beside the saved settings of the scopes above it, with no `settings.`\nprefix; on a connection or iClient form the template has no context. The options then\nload through `refreshOptionsOnChangesTo` or `typeAheadSearch`, or when the user\nrefreshes the field.\n","default":false},"refreshOptionsOnChangesTo":{"type":"array","items":{"type":"string"},"description":"For exportSelect fields with `supportHandlebars`, ids of the fields whose values the\ntemplated export uses: the options load once one of them has a value and reload whenever\none changes.\n"},"typeAheadSearch":{"type":"boolean","description":"For exportSelect fields, when true, re-runs the templated export as the user types, with\nthe typed text as this field's own value; an empty box clears the options, and typed\ntext is accepted as the value.\n","default":false},"showDelete":{"type":"boolean","description":"For keyvalue fields, determines whether to show a delete button for each key-value pair.\n"},"doNotAllowFutureDates":{"type":"boolean","description":"For date and datetime fields, restricts selection to dates not in the future.\n"},"skipTimezoneConversion":{"type":"boolean","description":"For datetime fields, prevents automatic timezone conversion of the date value.\n"},"options":{"type":"array","description":"For fields that present choices (select, multiselect, radiogroup, toggle), defines\nthe available options.\n","items":{"anyOf":[{"title":"Option group","type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"oneOf":[{"title":"String value","type":"string"},{"title":"Label-value pair","type":"object","properties":{"label":{"type":"string","description":"Display text for the option.\n"},"value":{"type":"string","description":"Value to store when this option is selected.\n"}}}]},"description":"Array of option values/labels to display in the selection control.\n"}}},{"title":"Label-value pair","type":"object","required":["label"],"properties":{"label":{"type":"string","description":"Display text for the option.\n"},"value":{"type":"string","description":"Value to store when this option is selected.\n"}}}]}},"visibleWhen":{"type":"array","description":"Conditional display rules that determine when this field should be visible.\nIf empty or not provided, the field is always visible.\n","items":{"type":"object","properties":{"field":{"type":"string","description":"The ID of another field whose value controls the visibility of this field. The\nsave doesn't check it: a rule naming an ID that isn't in the form counts as met,\nso the field stays visible.\n"},"is":{"type":"array","items":{"type":["string","boolean","number","null"]},"description":"Array of values - if the referenced field has any of these values,\nthis field will be visible. Values may be strings, booleans (for\ncheckbox/toggle fields), numbers (for numeric inputs), or null.\n"}}}}}}},"layout":{"type":"object","description":"Defines how the form fields are arranged and grouped in the UI.\nThe layout can organize fields into columns, sections, or other visual groupings.\n","properties":{"type":{"type":"string","description":"The type of layout to use for the form.\n","enum":["column","collapse","box","indent","tabWithoutSave","verticalTabWithoutSave"]},"fields":{"type":"array","items":{"type":"string"},"description":"Field IDs shown above the containers, without a heading.\n"},"containers":{"type":"array","description":"Array of container objects that group fields or contain nested containers.\nEach container can represent a column, box, indented section, or collapsible section.\n","items":{"type":"object","properties":{"type":{"type":"string","description":"The visual style of the container.\n","enum":["indent","box","collapse"]},"label":{"type":"string","description":"The heading text displayed for this container.\n"},"fields":{"type":"array","items":{"type":"string"},"description":"Array of field IDs that should be displayed in this container.\nEach ID must correspond to a key in the fieldMap object: the save doesn't check it,\nand an ID that doesn't renders nothing. A field the layout doesn't list isn't shown.\n"},"containers":{"type":"array","description":"Nested containers within this container. Allows for hierarchical organization\nof fields with different visual styles.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"The heading text displayed for this nested container.\n"},"fields":{"type":"array","items":{"type":"string"},"description":"Array of field IDs that should be displayed in this nested container.\n"}}}}}}}}}},"additionalProperties":true},"init":{"type":"object","description":"Configuration for custom JavaScript initialization that executes when the form is first loaded.\n\nThis object defines a JavaScript hook that prepares the form for use, sets initial field values,\nperforms validation, or otherwise customizes the form behavior before it is displayed to the user.\n\n**Function signature**\n\nThe initialization function is invoked with a single 'options' argument containing contextual information:\n```javascript\nfunction formInit(options) {\n  // Process options and return the form object\n  return options.resource.settingsForm.form;\n}\n```\n\n**Available context**\n\nThe 'options' argument provides access to:\n- `options.resource` - The current resource being configured\n- `options.parentResource` - The parent of the current resource\n- `options.grandparentResource` - The grandparent of the current resource\n- `options.license` - For integration apps, the license provisioned to the integration\n- `options.parentLicense` - For integration apps, the parent of the license\n- `options.sandbox` - Boolean flag indicating whether the script is running in a sandbox environment\n\n\n**Common uses**\n\n- Dynamically generate field options based on resource configuration\n- Pre-populate default values from related resources\n- Apply conditional logic that depends on resource properties\n- Add, remove, or modify form fields based on user permissions or account settings\n- Fetch external data to populate selection options\n- Implement complex validation rules that depend on resource context\n- Create branching form experiences based on user selections\n\n**Return value**\n\nThe function must return a valid form object that the UI can render.\nThrowing an exception will signal an error to the user.\n\nThe UI runs the function each time it opens the form and renders the returned form instead of\n`form`, then fills in the values from `settings`; a thrown error's message replaces the form.\n","properties":{"function":{"type":"string","description":"The name of the function to execute within the referenced script.\n\nThis property specifies which function to invoke from the script\nreferenced by _scriptId. The function will be called when the form\nis initialized and should handle any custom setup logic.\n\nThe function must follow the expected signature and return a valid form object.\n"},"_scriptId":{"type":"string","format":"objectId","description":"Reference to a predefined script resource containing the initialization function.\n\nThe referenced script should contain the function specified in the\n'function' property. On an integration, a flow group, a flow, an export, an import, or a\nconnection, an id that isn't a script in the same account (or, on an Integration App's\nresources, one the app shares) is refused with 422 `invalid_ref`. An iClient save\nstores the id without that check.\n"}}}}},"Settings":{"type":"object","description":"Configuration settings that can be accessed by hooks, filters, mappings and handlebars templates at runtime.\n\nIt enables customization of the resource's logic, allowing hooks, mappings, filters, and\nhandlebars to access and apply the settings at runtime.\n\n**Usage**\n\nThe settings object can store arbitrary JSON data that you want to save with the resource.\nWhile it's often populated through a form defined in the `settingsForm` field, you can also:\n\n- Directly provide JSON settings without using a form\n- Store configuration values used by hooks and templates\n- Create resource-specific constants and parameters\n- Maintain lookup tables or mapping structures\n- Define conditional logic parameters\n\n**Accessibility**\n\nSettings are available in:\n- All handlebars fields for building dynamic payloads\n- Field mapping expressions\n- JavaScript hooks via the options object\n- Filters and transformations\n\nAt runtime, the settings objects in the step's execution chain are gathered into a\nsingle `settings` context keyed by fixed scope keys. Flow runs populate the full\nchain: `settings.integration.*`, `settings.flowGrouping.*` (the flow group the flow\nbelongs to), `settings.flow.*`, `settings.connection.*`, `settings.iClient.*`, and\nthe running step's own scope — `settings.export.*` on exports/lookups,\n`settings.import.*` on imports (the key matches the step type). Steps executing\ninside a My API or a Tool receive only the step's own scope plus\n`settings.connection.*` / `settings.iClient.*`; the integration, flowGrouping, and\nflow scopes resolve empty there. The scope key is literal — resource display names\nand settingsForm section labels are never part of the path. The segments after the\nscope mirror the stored settings JSON key path exactly: a top-level field is\n`settings.<scope>.<fieldId>`, and nested objects add one segment per JSON key\n(e.g. `settings.flowGrouping.Customer.region` when the group's settings JSON nests\n`region` under `Customer`). References without a scope key\n(e.g. `{{settings.myField}}`) resolve to empty strings.\n\n**Best practices**\n\nFor non-technical users, create a custom form instead of editing the JSON directly.\nThis provides a user-friendly interface for updating settings without requiring JSON knowledge.\n","additionalProperties":true},"MockOutput":{"type":["object","null"],"description":"Sample data that simulates the output from an export for testing and configuration purposes.\n\nMock output allows you to configure and test flows without executing the actual export or\nwaiting for real-time data to arrive. This is particularly useful for:\n- Initial flow configuration and testing\n- Mapping development without requiring live data\n- Generating metadata for downstream flow steps\n- Creating realistic test scenarios\n- Documenting expected data structures\n\n**Structure**\n\nThe mock output must follow the integrator.io canonical format, which consists of a\n`page_of_records` array containing record objects. Each record object has a `record`\nproperty that contains the actual data fields.\n\n```json\n{\n  \"page_of_records\": [\n    {\n      \"record\": {\n        \"field1\": \"value1\",\n        \"field2\": \"value2\",\n        ...\n      }\n    },\n    ...\n  ]\n}\n```\n\n**Usage**\n\nIn a flow test run, and for an upstream lookup during a page-processor preview\n(`previewPageProcessor`), integrator.io substitutes this mock output for the export's live\nresponse instead of calling the source. A saved mock is not read anywhere else: a target\nlookup in that preview runs its live query, invoking the export\n(`POST /v1/exports/{_id}/invoke`) reads the source, and real runs ignore it. This allows you\nto:\n- Test mappings with representative data\n- Configure downstream flow steps without waiting for real data\n- Simulate various data scenarios\n\nWhere it is substituted, the records stand in for the RAW parsed response: the export's own\n`transform` and output `filter` still run on top of them, so downstream steps — and a\nlookup's results mapping — see the transformed, filtered records, not the shape stored here.\n\nIn a flow test run of an HTTP export with a saved mock (live probe, 2026-10-21), the export\nstill listed `request` and `response` stages: `request` held the request the export composes,\nand `response` held the mock under the source's URL, as did the test run's request log, which\nrecorded it as a 200. Read those stages as the substituted mock, not as a live call.\n\n`POST /v1/exports/preview` reads the `mockOutput` in its request body, not the saved one. For\nan export the preview supports (an HTTP export, for example), a body that carries it (as an\nexport read back with GET does) returns that snapshot as a lone `parse` stage without calling\nthe source; remove `mockOutput` from the body to preview live records.\n\n**Limitations**\n\n- Maximum of 10 records\n- Maximum size of 1 MB\n- Must follow the canonical format shown above\n\nMock output can be populated automatically from preview data (which is the parsed response,\nbefore `transform` / `filter`) or entered manually.\n","properties":{"page_of_records":{"type":"array","description":"Array of record objects in the integrator.io canonical format.\n\nEach item in this array represents one record that would be processed\nby the flow during execution.\n","items":{"type":"object","properties":{"record":{"type":"object","description":"Container for the actual record data fields.\n\nThe structure of this object will vary depending on the specific\nexport configuration and the source system's data structure.\n","additionalProperties":true}}}}}},"PreSave":{"type":"object","description":"Defines a JavaScript hook that executes before the resource is saved.\n\nThis hook allows for programmatic validation, transformation, or enrichment of the\nresource itself before it is persisted. It can be used to enforce business rules,\nset derived properties, or implement cross-field validations that can't be expressed\nthrough the standard UI.\n\n**Function signature**\n\nThe preSave function is invoked with a single 'options' argument containing:\n```javascript\nfunction preSave(options) {\n  // Process options and return the modified resource\n  return options.newResource;\n}\n```\n\n**Available context**\n\nThe 'options' argument provides access to:\n- `options.newResource` - The resource being saved (with pending changes)\n- `options.oldResource` - The previous version of the resource (before changes)\n- `options.sandbox` - Boolean flag indicating whether the script is running in a sandbox environment\n\n\n**Common uses**\n\n- Enforcing complex business rules across multiple fields\n- Automatically deriving field values based on other configuration\n- Performing validation that depends on external systems or data\n- Normalizing or standardizing configuration values\n- Adding computed or derived properties\n- Implementing versioning or change tracking\n- Dynamically looking up data using the Celigo API module to enrich configuration\n\n**Return value**\n\nThe function must return the newResource object (potentially modified) to be saved.\nThrowing an exception will prevent saving and signal an error to the user.\n","properties":{"function":{"type":"string","description":"The name of the function to execute within the referenced script.\n\nThis property specifies which function to invoke from the script\nreferenced by _scriptId. The function will be called just before\nthe resource is saved.\n\nThe function must follow the expected signature and return the resource object.\n"},"_scriptId":{"type":"string","format":"objectId","description":"Reference to a predefined script resource containing the preSave function.\n\nThe referenced script should contain the function specified in the\n'function' property. This script must be accessible within the user's account\nand have appropriate permissions.\n"}}},"Export":{"type":"object","required":["_id","name","adaptorType","createdAt","lastModified"],"description":"Export object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ExportBase"},{"$ref":"#/components/schemas/ResourceResponse"},{"$ref":"#/components/schemas/IAResourceResponse"},{"type":"object","properties":{"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"apim":{"$ref":"#/components/schemas/APIM"},"apiIdentifier":{"type":"string","readOnly":true,"description":"API identifier assigned to this export."},"asynchronous":{"type":"boolean","readOnly":true,"description":"Server-managed execution-mode flag set on creation."},"__linkedLookupCacheIds":{"type":"array","readOnly":true,"description":"Lookup caches linked to this export, managed by the platform.","items":{"type":"string","format":"objectId"}},"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the source resource this export was created from."},"_templateId":{"type":"string","format":"objectId","readOnly":true,"description":"Template this export was created from."},"draft":{"type":"boolean","readOnly":true,"description":"When true, this export is in draft state and has not been confirmed."},"draftExpiresAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the draft version of this export expires."},"debugUntil":{"type":"string","format":"date-time","readOnly":true,"description":"Turns on step debug capture until this time: while it lies in the future, every\nrequest this export sends is recorded in the step's debug log\n(`GET /v1/{flows|apis|tools}/{containerId}/{_id}/requests`). Read-only on PUT —\nwritten only by `PATCH /v1/exports/{_id}` on `/debugUntil`, at most 3600 seconds\nahead (`422` `update_not_allowed` beyond that); `remove` stops capture."}}}]},"ResourceResponse":{"type":"object","description":"Response","properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Unique identifier for the resource. Format is a 24-character hexadecimal string."},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was created. Set automatically and cannot be modified."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was last updated. Changes whenever any property is modified."},"deletedAt":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp when the resource was soft-deleted. When null or absent, the resource is active."}},"required":["_id"]},"IAResourceResponse":{"type":"object","description":"Integration app response fields for resources that are part of integration apps","properties":{"_integrationId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the specific integration instance that contains this resource.\n\nThis field is only populated for resources that are part of an integration app\ninstallation. It contains the unique identifier (_id) of the integration\nresource that was installed in the account.\n\nThe integration instance represents a specific installed instance of an\nintegration app, with its own configuration, settings, and runtime environment.\n\nThis reference enables:\n- Tracing the resource back to its parent integration instance\n- Permission and access control based on integration ownership\n- Lifecycle management (enabling/disabling, updating, or uninstalling)\n"},"_connectorId":{"type":"string","format":"objectId","readOnly":true,"description":"Integration App (connector) that defines this resource. Present only on resources an\nIntegration App owns. The app locks parts of these documents, and a PUT that changes a\nlocked field answers 200 but keeps the stored value, so read the document back to confirm\na change."}}},"AIDescription":{"type":"object","description":"AI-generated descriptions and documentation for the resource.\n\nThis object contains automatically generated content that helps users\nunderstand the purpose, behavior, and configuration of the resource without\nrequiring them to analyze the technical details. The AI-generated content\nis sanitized and safe for display in the UI.\n","properties":{"summary":{"type":["string","null"],"description":"Brief AI-generated summary of the resource's purpose and functionality.\n\nThis concise description provides a quick overview of what the resource does,\nwhat systems it interacts with, and its primary role in the integration.\nThe summary is suitable for display in list views, dashboards, and other\ncontexts where space is limited.\n\nMaximum length: 10KB\n"},"detailed":{"type":["string","null"],"description":"Comprehensive AI-generated description of the resource's functionality.\n\nThis detailed explanation covers the resource's purpose, configuration details,\ndata flow patterns, filtering logic, and other technical aspects. It provides\nin-depth information suitable for documentation, tooltips, or detailed views\nin the administration interface.\n\nThe content may include HTML formatting for improved readability.\n\nMaximum length: 10KB\n"},"generatedOn":{"type":["string","null"],"format":"date-time","description":"Timestamp indicating when the AI description was generated.\n\nThis field helps track the freshness of the AI-generated content and\ndetermine when it might need to be regenerated due to changes in the\nresource's configuration or behavior.\n\nThe timestamp is recorded in ISO 8601 format with UTC timezone (Z suffix).\n"}}},"APIM":{"type":"array","description":"Read-only field that stores information about the integration resources\npublished in the API Management (APIM) platform.\n\nThis field tracks the relationship between integrator.io resources and their\npublished counterparts in the APIM platform, which is\ntightly integrated with the Celigo UI. When resources are \"pushed\" to APIM,\nthis field is populated with the relevant identifiers and statuses.\n","items":{"type":"object","properties":{"apiId":{"type":"string","description":"Identifier for the API where this integrator.io resource is published in the APIM.\n\nThis is an APIM resource identifier (not prefixed with underscore like Celigo IDs)\nthat uniquely identifies the API in the API Management platform.\n"},"flowId":{"type":"string","description":"Identifier for the flow within the API where this integrator.io resource is linked.\n\nWhen an API has multiple integrator.io resources linked, each resource is associated\nwith a specific flow in the API, identified by this field. This is an APIM\nresource identifier.\n"},"status":{"type":"string","description":"Indicates the publishing stage of the integrator.io resource in APIM.\n\nPossible values:\n- 'oaspending': The resource is published but the OpenAPI Specification (OAS) is not\n  yet published. The apiId will be updated with the API ID created in APIM.\n- 'published': The OpenAPI Specification for the integrator.io resource has been\n  successfully uploaded to APIM.\n","enum":["oaspending","published"]}}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"400-bad-request":{"description":"Bad request. The server could not understand the request because of malformed syntax or invalid parameters.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"422-unprocessable-entity":{"description":"Unprocessable entity. The request was well-formed but was unable to be followed due to semantic errors.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/exports":{"post":{"summary":"Create an export","description":"Creates a new export configuration that can be used to retrieve data from applications\nor external sources.\n","operationId":"createExport","tags":["Exports"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Request"}}}},"responses":{"201":{"description":"Export created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Export"}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
````

## Get an export

> Returns the complete configuration of a specific export.<br>

````json
{"openapi":"3.2.0","info":{"title":"Exports","version":"1.0.0"},"tags":[{"name":"Exports","description":"Exports retrieve data from source systems — on a schedule, in delta mode, in real time,\non demand, or as a file/blob transfer — package the results into ≤ 5-MB pages, and pass\neach page to downstream flow steps. Depending on configuration, an export surfaces in the\nFlow Builder as an export, a real-time listener, a file transfer, or a mid-flow lookup.\n\n## Export schema\n\n{% openapi-schemas spec=\"export\" schemas=\"Export\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"Export":{"type":"object","required":["_id","name","adaptorType","createdAt","lastModified"],"description":"Export object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ExportBase"},{"$ref":"#/components/schemas/ResourceResponse"},{"$ref":"#/components/schemas/IAResourceResponse"},{"type":"object","properties":{"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"apim":{"$ref":"#/components/schemas/APIM"},"apiIdentifier":{"type":"string","readOnly":true,"description":"API identifier assigned to this export."},"asynchronous":{"type":"boolean","readOnly":true,"description":"Server-managed execution-mode flag set on creation."},"__linkedLookupCacheIds":{"type":"array","readOnly":true,"description":"Lookup caches linked to this export, managed by the platform.","items":{"type":"string","format":"objectId"}},"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the source resource this export was created from."},"_templateId":{"type":"string","format":"objectId","readOnly":true,"description":"Template this export was created from."},"draft":{"type":"boolean","readOnly":true,"description":"When true, this export is in draft state and has not been confirmed."},"draftExpiresAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the draft version of this export expires."},"debugUntil":{"type":"string","format":"date-time","readOnly":true,"description":"Turns on step debug capture until this time: while it lies in the future, every\nrequest this export sends is recorded in the step's debug log\n(`GET /v1/{flows|apis|tools}/{containerId}/{_id}/requests`). Read-only on PUT —\nwritten only by `PATCH /v1/exports/{_id}` on `/debugUntil`, at most 3600 seconds\nahead (`422` `update_not_allowed` beyond that); `remove` stops capture."}}}]},"ExportBase":{"type":"object","description":"Writable export fields shared by the request and response schemas.","properties":{"name":{"type":"string","maxLength":100,"description":"Display name for the export, shown in the flow builder, job history, and error logs.\nDescriptive, unique names indicating the data source and purpose make large accounts easier to manage."},"description":{"type":["string","null"],"description":"Free-text summary of what the export retrieves and why. Shown in the UI and available\nto AI agents for context; has no effect on execution.","maxLength":5120},"_connectionId":{"format":"objectId","type":["string","null"],"description":"Connection this export uses to reach the source system. The connection's type must be\ncompatible with the export's `adaptorType` (e.g. an `HTTPExport` needs an `http` connection;\n`NetSuiteExport` and `NetSuiteHTTPExport` both need a `netsuite` connection).\nServer-required unless `type` is `webhook` or `simple` (those receive data instead of\nfetching it) or `adaptorType` is `TopicExport` (Event Streams listeners are delivered\nby the platform itself and must omit it). A `WebhookExport` saved without `type: webhook`\nis refused with 422 \"_connectionId is required unless type is [webhook|simple]\"; a webhook\nexport that does carry a connection saves and updates normally."},"adaptorType":{"type":"string","description":"Selects the adaptor technology that executes this export, which determines the compatible\nconnection types and which adaptor-specific configuration object must also be supplied\n(e.g. set `salesforce` when using `SalesforceExport`).","enum":["HTTPExport","FTPExport","AS2Export","S3Export","NetSuiteExport","NetSuiteHTTPExport","SalesforceExport","JDBCExport","RDBMSExport","MongodbExport","DynamodbExport","WrapperExport","SimpleExport","WebhookExport","FileSystemExport","TopicExport","RESTExport"]},"nsDomainType":{"type":"string","enum":["suitetalk","restlet"],"description":"Selects which NetSuite REST host a `NetSuiteHTTPExport` calls. The server derives the\nfull base URL from the connection's NetSuite account\n(`https://<account>.suitetalk.api.netsuite.com` or `https://<account>.restlets.api.netsuite.com`)\nand signs each request with the connection's token-based credentials, so `http.relativeURI`\ncarries the complete path starting at `/services/rest/...` or `/app/site/hosting/restlet.nl`.\nIgnored on other adaptor types."},"type":{"type":["string","null"],"description":"Operational mode of the export. When omitted, the export retrieves all available records —\nthe standard batch mode, which is also the right choice for parsing structured files\n(CSV/XML/JSON) into records. Most modes take their matching configuration object (e.g. a\n`webhook` object when `type` is `webhook`); an HTTP or SQL `delta` export saves without a\n`delta` object because its request carries `{{lastExportDateTime}}`, though\n`delta.startDate` sets its first window. It still needs `type: delta`: on an export\nwith no `type`, `{{lastExportDateTime}}` and `{{currentExportDateTime}}` render\nempty on every run, so a request that carries them goes out with blank values each time\n(live-verified 2026-10-22). Use `blob` only to transfer raw files without parsing their\ncontents.","enum":["webhook","test","delta","once","tranlinedelta","simple","blob","distributed","stream","all"]},"pageSize":{"type":["integer","null"],"description":"Number of records per page streamed to downstream flow steps when the export is the\nflow's source step. Pages are additionally capped at 5 MB regardless of record count, so\nvery large records may produce smaller pages. The server does not validate the value.\nA lookup (`isLookup: true`) runs to completion and hands every result to the incoming\nrecord as one response, capped at 5 MB in total, so the value does not change how many\nresults a lookup returns; a result set larger than that belongs in a standalone export\nstep. In a test run or preview the value also caps the records fetched (default 20).\nOn a webhook listener the records of each incoming request are paged separately, before\nany hook or downstream step runs.","default":20},"dataURITemplate":{"type":"string","description":"Handlebars template that builds a link back to each record in the source application's UI\n(e.g. `https://my.salesforce.com/lightning/r/Contact/{{record.Id}}/view`). The resolved\nURL is stored with error records in job history so users can jump straight to the record."},"traceKeyTemplate":{"type":["string","null"],"description":"Handlebars template that overrides how each record's unique trace key is generated, used\nto track records through the flow and match retries to prior errors (e.g.\n`{{join \"_\" record.customerId record.orderId}}`). When omitted, the system picks a unique\nfield automatically from the record's top level, by exact field name (`GET\n/v1/flows/{_id}/traceKey` serves the pattern). The rendered value of a template is stamped\non each record as `io_custom_trace_key` and travels downstream with it. If a transform\nreshapes the data first, omit the `record.` prefix. Trace keys are capped at 256 characters;\na longer key is stored truncated from the middle, keeping the beginning and end of the\nvalue. Downstream imports use the trace key as their default concurrency lock key (a blank\nkey means no lock); it never de-duplicates — a record emitted twice with the same key is\nprocessed twice."},"skipRetries":{"type":"boolean","description":"When true, the platform does not retain the source data needed to retry failed records,\nreducing stored data at the cost of being unable to reprocess errors from this export.\nShown as \"Do not store retry data\" in the UI. Defaults to false."},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"isLookup":{"type":"boolean","description":"When true, the export runs as a mid-flow lookup: it executes once per incoming record,\nusing the input record's fields to parameterize the request, and passes the results to\nsubsequent steps. When false, the export runs as a standalone data source. A lookup's\nresults are not paged through the flow: every page the adaptor fetches is merged into one\nresponse for the record, up to 5 MB, so `pageSize` does not limit or extend what a lookup\nreturns. A lookup that must return more than that is better modeled as a standalone\nexport step."},"groupByFields":{"$ref":"#/components/schemas/GroupBy"},"delta":{"$ref":"#/components/schemas/Delta"},"test":{"$ref":"#/components/schemas/Test"},"once":{"$ref":"#/components/schemas/Once"},"webhook":{"$ref":"#/components/schemas/Webhook"},"simple":{"$ref":"#/components/schemas/Simple"},"distributed":{"$ref":"#/components/schemas/Distributed"},"cdc":{"$ref":"#/components/schemas/Cdc"},"topic":{"$ref":"#/components/schemas/Topic"},"filesystem":{"$ref":"#/components/schemas/FileSystem"},"http":{"$ref":"#/components/schemas/Http"},"file":{"$ref":"#/components/schemas/File"},"salesforce":{"$ref":"#/components/schemas/Salesforce"},"as2":{"$ref":"#/components/schemas/AS2"},"dynamodb":{"$ref":"#/components/schemas/DynamoDB"},"ftp":{"$ref":"#/components/schemas/FTP"},"jdbc":{"$ref":"#/components/schemas/JDBC"},"mongodb":{"$ref":"#/components/schemas/MongoDB"},"netsuite":{"$ref":"#/components/schemas/NetSuite"},"rdbms":{"$ref":"#/components/schemas/RDBMS"},"s3":{"$ref":"#/components/schemas/S3"},"wrapper":{"$ref":"#/components/schemas/Wrapper"},"parsers":{"$ref":"#/components/schemas/Parsers"},"filter":{"description":"Filter applied to each record after the export's transform and before its preSavePage\nhook. Records that match continue through the flow; records that don't are silently\ndropped. Filter expressions reference the transformed record's fields.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"inputFilter":{"description":"Filter applied to incoming records before a lookup export queries the external system.\nOnly matching input records trigger lookup calls; non-matching records pass through the\nstep unenriched, which cuts unnecessary API traffic. Only relevant when `isLookup` is true.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"mappings":{"description":"Field mappings applied to each incoming record before a lookup HTTP request is made,\nreshaping the upstream record into the structure the lookup target API expects. Only\nsupported when `isLookup` is true and `adaptorType` is `HTTPExport` or\n`NetSuiteHTTPExport`; do not set it on source exports or other lookup adaptors.","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"transform":{"description":"Transformation that reshapes records before they leave this step — source exports\ntransform the retrieved records, lookup exports transform the lookup results. Commonly\nused to flatten nested lookup responses (e.g. `results[0].id` → `id`); merging results\nback into source records is handled separately by the flow's response mapping.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the export lifecycle for\ntransformations, validation, and filtering beyond what configuration alone can express.\nA hook name not listed here (an import's `preMap`, for example) is dropped on save\nwithout an error, so read `hooks` back after saving.","properties":{"preSavePage":{"type":"object","description":"Hook that runs on each page of records after the export's transform and output\nfilter, before the page is sent to downstream steps; commonly used to flatten\nstructures, drop unwanted records, or add computed fields. It receives the page as\n`options.data` (with `errors`, `pageIndex`, `configuration` and `settings`) and returns\n`{data, errors, abort, newErrorsAndRetryData}`. Return that object, not the array: a\nhook that returns `options.data` itself, or a filtered copy, drops the page without an\nerror, and the run completes with 0 records, 0 errors and 0 pages. An exception thrown\nhere fails every record on that page, each with its own retry data, and later pages\nstill run.","properties":{"function":{"type":"string","description":"Function to invoke within the referenced script."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`. Must be a script in this\naccount: any other id fails the save with 422 (\"Only a script owned by the user or\na script shared by the Integration App can be used at hooks.preSavePage._scriptId.\")."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments."},"configuration":{"type":"object","description":"Static parameters passed to the hook function at runtime, letting one script be\nreused across exports with different settings."}}},"preParse":{"type":"object","description":"Hook that runs on file-based exports after the file is downloaded and before it is\nparsed: it receives the raw file text as `preParseData` together with `fileMeta` and\nreturns both, modified or not, for the configured parser to read. Used to normalize a\nnon-standard file — fix a header row, strip a preamble, convert a format — before\nparsing; an exception thrown here fails the run.","properties":{"function":{"type":"string","description":"Function to invoke within the referenced script."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments."},"configuration":{"type":"object","description":"Static parameters passed to the hook function at runtime, letting one script be\nreused across exports with different settings."}}}}},"settingsForm":{"$ref":"#/components/schemas/Form"},"settings":{"$ref":"#/components/schemas/Settings"},"mockOutput":{"$ref":"#/components/schemas/MockOutput"},"_ediProfileId":{"type":"string","format":"objectId","description":"EDI profile this export uses to parse incoming X12 or EDIFACT documents into structured\nJSON — it supplies the envelope qualifiers, delimiters, version, and validation rules.\nSet it when the export parses or validates EDI files (a save with a `delimited/x12` file\ndefinition at `version` `\"2\"` and no profile fails with 422 `missing_ediProfile`); omit\nit otherwise. Accepted on the file-reading exports — FTP, AS2, S3 and HTTP file mode\n(`http.type: \"file\"`)."},"_postParseListenerId":{"type":"string","format":"objectId","description":"Webhook export invoked once per file after EDI parsing, on both success and failure\n(errors are included in the payload when parsing fails). Primarily used to send\nfunctional acknowledgements (997/999) to trading partners. Accepted on the file-reading\nexports that parse EDI — FTP, AS2, S3 and HTTP file mode (`http.type: \"file\"`)."},"externalId":{"type":["string","null"],"description":"External identifier for correlating the export with a record in another system."},"_integrationId":{"type":["string","null"],"format":"objectId","description":"Not stored on an export: a value sent on create or on a full update is dropped, and a GET\ndoesn't return the field. An export isn't owned by an integration; to find the flows and\nother resources that use it, call `GET /v1/exports/{_id}/dependencies`."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this export was created from, set when the export is part of an installed\nintegration app. The app's install sets it: a create that sends it is refused with 403\n(\"Cannot create an Integration App resource\")."},"unencrypted":{"type":"object","description":"Custom configuration values stored without encryption and returned in API responses."},"useTechAdaptorForm":{"type":"boolean","description":"When true, the UI presents the full technical adaptor form for this export instead of\nthe simplified assistant form."},"rawData":{"type":"string","description":"Key referencing the raw sample payload captured for preview and testing."},"preSave":{"$ref":"#/components/schemas/PreSave"},"assistant":{"type":"string","description":"Identifier for the connector assistant used to configure this export. For the cloud\nfile providers it also selects the provider-specific location rules —\n`azuredatalakestoragegen2` (container in `file.directory.name`, path in\n`http.relativeURI`) and `googlecloudstorage` (bucket in `file.directory.storageRootId`,\nprefix in `file.directory.id`)."},"assistantMetadata":{"type":["object","string"],"additionalProperties":true,"description":"Metadata associated with the connector assistant configuration."},"sampleData":{"type":["string","object","array","null"],"description":"Sample data payload used for previewing and testing the export."},"sampleHeaders":{"type":"array","description":"Sample HTTP headers used for previewing and testing the export.","items":{"type":"object","properties":{"name":{"type":"string","description":"Header name."},"value":{"type":"string","description":"Header value."}}}},"sampleQueryParams":{"type":"array","description":"Sample query parameters used for previewing and testing the export.","items":{"type":"object","properties":{"name":{"type":"string","description":"Query parameter name."},"value":{"type":"string","description":"Query parameter value."}}}}}},"OneToMany":{"type":"boolean","description":"When true, the step runs once per child record instead of once per incoming record.\n`pathToMany` names the array field that holds the children inside an object record; when\nthe incoming record is itself an array (grouped or row-based data), leave `pathToMany` blank\nand each element becomes a record. The fan-out is scoped to this step: afterwards the children\nare re-joined into the record's original shape — the object with its array, or the array of rows —\ncarrying any response-mapping enrichment, and that re-joined record is what the next step\nreceives. Not for locating records in an export's HTTP response; use\n`http.response.resourcePath` for that. Applies to steps that receive a record — imports and\nlookups (an export referenced as a page processor of a flow, API, or Tool). A standalone export\n(a flow's page generator) has no incoming record, so the fields are saved but have no effect on\nthe records it produces; to emit one record per array element from a standalone export, use a\n`hooks.preSavePage` script, or for a file-definition export make the repeating segment the\nrecord boundary in the definition's rules.\n","default":false},"PathToMany":{"type":"string","description":"Path to the array of child records inside an object record when `oneToMany` is true, in dot\nnotation (`items`, `lines.lineItems`). Leave blank when the incoming record is itself an array\n(grouped or row-based data) — each element is then a child record. A path that does not resolve\nto an array processes zero records and reports success. Read only on steps that receive a\nrecord (imports, lookup exports); on a standalone export it is saved and ignored — see `oneToMany`.\n"},"GroupBy":{"type":"array","description":"Specifies which fields to use for grouping records in the export results. When configured, records with\nthe same values in these fields will be grouped together and treated as a single record by downstream\nsteps in your flow.\n\nFor example:\n- Group sales orders by customer ID to process all orders for each customer together\n- Group journal entries by accounting period to consolidate related transactions\n- Group inventory items by location to process inventory by warehouse\n\nWhen grouping is used, the export's page size determines the maximum number of groups per page, not individual\nrecords. Note that effective grouping typically requires that records with the same group field values appear\ntogether in the export data.\n\nNot a webhook listener feature: on a `webhook` export the platform applies it only within each page, after\nthe request has been paged, and only to adjacent rows — the field is not offered for listeners in the UI.\n","items":{"type":"string"}},"Delta":{"type":"object","description":"Configures incremental exports that retrieve only records created or modified since the\nflow's delta checkpoint; omit it for other export types. Adaptors that compare a record field\nname it in `dateField`, while HTTP exports and SQL queries carry {{lastExportDateTime}} in the\nrequest and save without a delta object. Each run's window runs from {{lastExportDateTime}}\n(the checkpoint) to {{currentExportDateTime}} (the time the run started). A first run is not a\nfull export: with no `startDate`, its {{lastExportDateTime}} is the run's own timestamp, so it\npicks up no earlier changes. The checkpoint moves to a run's window end only when the run\nexports records, so a run that exports nothing leaves it where it was, and after a failed run\nthe next run reuses the last checkpoint. On success, when `POST /v1/flows/{_id}/run` sends\n`export.endDate`, the flow's checkpoint is left at that value, so an end date in the past\nmoves the next run's lower bound back.","properties":{"dateField":{"type":"string","description":"Record timestamp field(s) compared against the last successful run time to identify\nchanged records. Accepts a single field or multiple comma-separated fields, processed\nsequentially — useful when different operations update different timestamp fields.\nIf the flow's own downstream steps update the exported records, add export criteria\nthat exclude already-processed records (or use a creation-time field for process-once\nflows) so each run doesn't re-export what the previous run wrote. Not supported on\nHTTP exports: embed {{lastExportDateTime}} in the relativeURI or body instead; an HTTP\nexport saved with dateField is refused with 422 \"Unsupported dateField in\nexport.delta.dateField\". For Salesforce this\nfield is required and defaults to the standard timestamp fields (LastModifiedDate,\nCreatedDate, SystemModstamp, LastActivityDate, LastViewedDate, LastReferencedDate) plus\nany custom timestamp fields."},"dateFormat":{"type":"string","description":"Moment.js format string applied to both delta variables, {{lastExportDateTime}} and\n{{currentExportDateTime}}, in HTTP requests and SQL queries alike. Leave unset unless the\nsource system requires a non-ISO8601 format; by default both render as ISO 8601 UTC with\nmilliseconds. Date-only formats truncate the time portion, widening the filter window."},"lagOffset":{"type":"integer","description":"Buffer in milliseconds subtracted from the last successful run timestamp, creating an\noverlapping window that catches records still propagating when the previous run executed.\nSet it when records created or modified near the run time are occasionally skipped due to\nreplication or indexing delays. Keep it as low as possible — larger values reprocess\nredundant records. A negative value shifts the window forward (look-ahead) instead of back."},"startDate":{"type":["string","null"],"format":"date-time","description":"Lower-bound cutoff for the first run. Omitting it doesn't export history: the first run\nthen starts at its own timestamp, an empty window. Set it to the earliest change the first\nrun should pick up. Later runs start from the flow's checkpoint, the end of the last run\nthat exported records."}}},"Test":{"type":"object","description":"Configures test exports that cap the number of records retrieved, for safely developing and\nvalidating against small data samples. Required when the export's type is \"test\"; omit for\nother export types. A test export behaves like a standard export in every other way (filters,\npagination, processing) and stores no state between runs.","properties":{"limit":{"type":"integer","default":1,"description":"Caps the total records a test run processes, counted on top-level records before\noneToMany processing; the maximum exists to prevent accidentally processing large\ndatasets during development. The cap applies across pages — processing stops once the\nlimit is reached regardless of pageSize. When transitioning to production, leave this in\nplace and change the export's type field; the limit then no longer applies.","minimum":1,"maximum":100}}},"Once":{"type":"object","description":"Configures flag-based exports that skip records an earlier run already processed. Required\nwhen the export's type is \"once\"; omit for other export types. Each run retrieves records\nwhere the tracking boolean field is false, then sets that field to true after successful\nprocessing so later runs skip them; if a run fails, the flags are left unchanged and the\nrecords are retried automatically on the next run.","properties":{"booleanField":{"type":"string","description":"API field name of the boolean/checkbox in the source system that tracks processed\nrecords: the export selects only records where this field is false, then sets it to true\nin batches after each successfully processed page. The field must be writeable by the\nexport's connection. Ensure no other process updates the same field — use a separate\nflag per export process — or records may be skipped unexpectedly."}}},"Webhook":{"type":"object","description":"Configuration for webhook listeners that receive data through incoming HTTP requests. Required when the export's `type` is `webhook`; omit for other export types. Senders POST to the export's listener URL, `https://api.integrator.io/v1/exports/{_id}/data` (or the same path on the account's regional API host); the platform validates each request using the configured `verify` method, passes the payload to subsequent flow steps, and returns a configurable HTTP response to the caller. A request is answered 204 by default when accepted, 401 when it fails verification, and 403 `unable_to_run_flows` while every flow the export feeds is disabled; a refused request is not queued, so the sender must resend it.","properties":{"provider":{"type":"string","description":"Source application sending the webhook, used to pre-configure security settings and payload parsing for that platform. Use `custom` (the default when omitted) for unlisted providers or when you need full manual control over the security configuration. Provider-specific choices may require credentials (tokens, keys) mandated by that platform.","enum":["github","shopify","travis","travis-org","slack","dropbox","onfleet","helpscout","errorception","box","stripe","aha","jira","pagerduty","postmark","mailchimp","intercom","activecampaign","segment","recurly","shipwire","surveymonkey","parseur","mailparser-io","hubspot","integrator-extension","custom","sapariba","happyreturns","typeform"]},"verify":{"type":"string","description":"Verification method applied to every incoming request before processing; required for all webhook exports. Each method needs companion fields: `hmac` requires `key`, `algorithm`, `encoding`, and `header` (except on connector-backed webhooks, where the connector definition supplies them); `token` requires `token`, with `tokenLocation` defaulting to `body` and selecting which location-specific field applies; `basic` requires `username` and `password`; `secret_url` requires `token`. Prefer `hmac` when the source system supports it — it is the most secure option; `secret_url` relies only on URL obscurity and suits non-sensitive data or testing.","enum":["token","hmac","basic","secret_url"]},"token":{"type":"string","description":"Shared secret used when `verify` is `token` or `secret_url`. For `token` verification, the value found at `path` in each request must exactly match (case- and whitespace-sensitive) or the request is rejected with a 401. For `secret_url`, the token is embedded in the webhook URL to create a hard-to-guess endpoint — generate a random, high-entropy value and treat it as a sensitive credential."},"algorithm":{"type":"string","description":"Hashing algorithm used to validate HMAC signatures when `verify` is `hmac`. Must match the algorithm the webhook sender uses — a mismatch causes every request to be rejected. Use `sha256` unless the provider explicitly requires another value.","enum":["sha1","sha256","sha384","sha512"]},"encoding":{"type":"string","description":"Encoding of the HMAC signature value when `verify` is `hmac`. Must match the encoding the webhook sender uses — a mismatch causes requests to be rejected with a 401 even when the signature is otherwise correct. With `provider: custom`, `hex` is compared as lowercase hex: an uppercase hex signature is rejected.","enum":["hex","base64"]},"key":{"type":"string","description":"Secret used to validate signatures when `verify` is `hmac`. The system computes a signature of the request body using this key and the configured `algorithm`, then compares it with the signature sent in the request header named by `header`. Treat it as a highly sensitive credential — never expose it in repositories or logs."},"header":{"type":"string","description":"Name of the request header that carries the HMAC signature when `verify` is `hmac`. Must match the header name the webhook sender uses (header names are case-insensitive); requests without this header are rejected with a 401. With `provider: custom` the header value must be the bare digest in the configured `encoding`: a value with a `sha256=` prefix is rejected with a 401."},"tokenLocation":{"type":"string","enum":["body","header","queryParam"],"default":"body","description":"Where the verification token is found when `verify` is `token`. Each location uses a different companion field: `body` uses `path`, `header` uses `tokenHeaderName` (and `tokenHeaderScheme`), `queryParam` uses `tokenQueryParamName`."},"path":{"type":"string","description":"JSON path into the request body holding the verification token when `verify` is `token` and `tokenLocation` is `body` (e.g. `meta.token`). The value at this path must exactly match `token` or the request is rejected."},"tokenHeaderName":{"type":"string","description":"Request header carrying the verification token when `verify` is `token` and `tokenLocation` is `header`."},"tokenHeaderScheme":{"type":"string","enum":["bearer","custom","none"],"default":"bearer","description":"Scheme prefixing the token in the header when `tokenLocation` is `header`."},"customTokenScheme":{"type":"string","description":"Custom header scheme prefix used when `tokenHeaderScheme` is `custom`."},"tokenQueryParamName":{"type":"string","description":"Query parameter carrying the verification token when `verify` is `token` and `tokenLocation` is `queryParam`."},"_httpConnectorId":{"type":"string","format":"objectId","description":"HTTP connector backing this listener when the webhook is provided by an assistant/connector (e.g. Slack); set by the platform for connector-backed listeners."},"requestMediaType":{"type":"string","enum":["json","xml","csv","urlencoded","plaintext"],"description":"Overrides how the incoming request body is parsed when the provider sends a non-standard content type. When omitted, the body is parsed by its Content-Type header."},"pathToRecords":{"type":"string","description":"JSON path into an object payload to the array whose elements become the records. When omitted, a top-level JSON array is emitted as one record per element and an object payload is emitted as a single record."},"includeParentData":{"type":"boolean","description":"When true and `pathToRecords` is set, each emitted record also carries the surrounding parent fields from the payload."},"username":{"type":"string","description":"Username half of the credentials when `verify` is `basic`. Incoming requests must include an `Authorization: Basic` header carrying the base64-encoded `username:password` pair. Use only over HTTPS to prevent credential interception."},"password":{"type":"string","description":"Password half of the credentials when `verify` is `basic`, validated together with `username` from the request's `Authorization` header. Use a strong, unique value and treat it as a sensitive credential."},"successStatusCode":{"type":"integer","description":"HTTP status code returned to the caller after successful processing; must be a valid 2xx code. The default 204 returns no response body and causes `successBody` to be ignored — set 200 or 202 when the caller needs a response body or expects a specific code.","default":204},"successBody":{"type":"string","description":"Response body returned to the caller after successful processing; ignored when `successStatusCode` is 204. Content type is set by `successMediaType`, and the value can be static text or structured JSON/XML, including handlebars expressions for dynamic values."},"successMediaType":{"type":"string","description":"Sets the Content-Type header on successful webhook responses. Only takes effect when a `successBody` is returned (a status code other than 204), and must match the actual format of that body.","default":"json","enum":["json","xml","plaintext"]},"successResponseHeaders":{"type":"array","description":"Custom headers added to successful webhook responses — for example CORS headers or correlation IDs. Headers defined here take precedence over automatically set headers such as Content-Type, and values support handlebars expressions for dynamic content.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the header to set on successful webhook responses; headers defined here take precedence over automatically set headers such as Content-Type."},"value":{"type":"string","description":"Value sent for the header; supports handlebars expressions for dynamic content."}}}},"challengeResponseHeaders":{"type":"array","description":"Custom headers returned for webhook subscription verification (challenge) requests, which providers send before delivering real events. Required header values vary by provider — consult the provider's documentation, since incorrect challenge headers can prevent the subscription from being verified.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the header to set on challenge (subscription verification) responses."},"value":{"type":"string","description":"Value sent for the header; consult the provider's documentation for required challenge header values."}}}},"challengeSuccessBody":{"type":"string","description":"Response body returned for webhook subscription verification (challenge) requests. Many providers require echoing back a challenge value from the request, which handlebars expressions can access — for example `{{hub.challenge}}` (Facebook/Instagram) or `{\"challenge\":\"{{challenge}}\"}` (Slack). An incorrect challenge response prevents the subscription from being verified."},"challengeSuccessStatusCode":{"type":"integer","description":"HTTP status code returned for webhook subscription verification (challenge) requests. Most providers expect the default 200; change it only when the provider's verification explicitly requires a different code.","default":200},"challengeSuccessMediaType":{"type":"string","description":"Sets the Content-Type header on challenge responses. Must match both the format of `challengeSuccessBody` and the provider's requirements — for example, Slack expects `json` while Facebook/Instagram verification expects `plaintext`.","default":"json","enum":["json","xml","plaintext"]}},"if":{"properties":{"verify":{"const":"hmac"}},"required":["verify"],"not":{"required":["_httpConnectorId"]}},"then":{"required":["key","algorithm","encoding","header"]},"else":{"if":{"properties":{"verify":{"const":"token"}},"required":["verify"]},"then":{"required":["token"],"if":{"properties":{"tokenLocation":{"const":"body"}},"required":["tokenLocation"]},"then":{"required":["path"]},"else":{"if":{"properties":{"tokenLocation":{"const":"header"}},"required":["tokenLocation"]},"then":{"required":["tokenHeaderName"]},"else":{"if":{"properties":{"tokenLocation":{"const":"queryParam"}},"required":["tokenLocation"]},"then":{"required":["tokenQueryParamName"]},"else":{"required":["path"]}}}},"else":{"if":{"properties":{"verify":{"const":"basic"}},"required":["verify"]},"then":{"required":["username","password"]},"else":{"if":{"properties":{"verify":{"const":"secret_url"}},"required":["verify"]},"then":{"required":["token"]}}}}},"Simple":{"type":"object","description":"Configuration for `simple` (data-loader) exports — exports that accept manually uploaded\nfiles through the data-loader UI instead of fetching data from a connection. Relevant only\nwhen the export's `type` is `simple`; the uploaded content is parsed using the same file\nsettings as file-based exports.","properties":{"file":{"$ref":"#/components/schemas/File"}}},"File":{"type":"object","description":"Controls how files are parsed, filtered, and processed across all file-based exports\n(FTP/SFTP, Amazon S3, simple file uploads, and other file sources). The type field selects\nthe file format and determines which format-specific object (csv, json, xlsx, xml, or\nfileDefinition) must be configured; the output field selects whether files are parsed into\nrecords, transferred as blobs, or listed as metadata only. The filter object selectively\nskips files before processing.","properties":{"encoding":{"type":"string","description":"Character encoding used to read and parse file content. If the encoding is unknown, try\nutf8 first (the default), then win1252 for Western-language files with garbled\ncharacters; consider the geographic origin of the data when selecting.","enum":["utf8","win1252","utf-16le","gb18030","macroman","iso88591","shiftjis"]},"type":{"type":"string","description":"Format of the files being processed; determines which format-specific configuration\nobject (csv, json, xlsx, xml, or fileDefinition) must be populated — other format\nobjects are ignored. Required for all file-based exports except blob exports (export\ntype \"blob\" or output \"blobKeys\").","enum":["csv","json","xlsx","xml","filedefinition"]},"output":{"type":"string","description":"Processing mode for retrieved files: parse contents into records, transfer files as\nunparsed blobs, or return only file metadata. Determines what data is passed to\nsubsequent flow steps.","enum":["records","metadata","blobKeys"]},"skipDelete":{"type":"boolean","default":false,"description":"When true, source files remain on the file server after processing; when false (the\ndefault), files are deleted after successful processing. Files that fail processing are\nnever deleted, regardless of this setting. Enable retention when files must be processed\nby other flows or systems, or kept for compliance."},"compressionFormat":{"type":"string","description":"Compression format of incoming files, which are decompressed before any other processing\n(parsing, filtering). Set this only when the source always delivers compressed files —\nif a file marked as compressed is not actually compressed, processing fails. Leave unset\nwhen files arrive uncompressed or only sometimes compressed.","enum":["gzip","zip"]},"purgeInternalBackup":{"type":"boolean","description":"When true, Celigo keeps no internal backup copies of files processed by this export;\nwhen false (the default), copies are retained for your account's retention period and\nare available for reprocessing or troubleshooting. Applies only to this export and only\nto Celigo's internal copies — source files are governed by skipDelete. Enable for highly\nsensitive data or zero-retention policies; without backups, recovery may require\nre-obtaining files from the source system."},"decrypt":{"type":"string","description":"Decryption applied to incoming files before any other processing; decryption runs before\ndecompression, and a decryption failure fails the file's processing entirely. The\nconnection must already be configured with the private key (and passphrase, if\napplicable) matching the public key used to encrypt the files. Only PGP/GPG encryption\nis currently supported.","enum":["pgp"]},"batchSize":{"type":["integer","null"],"description":"Number of files retrieved per batch; if a batch fails, the whole batch is retried\n(stored as `null` when not configured). Use\nlower values (10-50) for large files to reduce timeout and memory pressure, and higher\nvalues for many small files to improve throughput. Controls file retrieval only — record\npaging is governed by the export's pageSize.","maximum":1000},"sortByFields":{"type":"array","description":"Sorts the records parsed from each file before they are processed, establishing a\ndeterministic processing order (for example, chronological or priority-based). Sorting\nhappens after parsing but before any filtering or grouping, and does not modify the\nsource files. Sorting by the same fields used in groupByFields improves grouping\nperformance.","items":{"type":"object","properties":{"field":{"type":"string","description":"Record field to sort by; use dot notation for nested properties (e.g.\ncustomer.name). Field names are case-sensitive."},"descending":{"type":"boolean","description":"When true, sorts this field in descending order (newest/highest first); when false\nor omitted, sorts ascending. Directions can be mixed across fields in a\nmulti-field sort."}}}},"groupByFields":{"$ref":"#/components/schemas/GroupBy"},"groupEmptyValues":{"type":"boolean","description":"When true, records whose groupByFields values are empty are still grouped together rather\nthan each forming its own group; when false (the default), empty-keyed records are not\ngrouped. Only relevant when groupByFields is set."},"csv":{"type":"object","description":"Parsing settings for delimiter-separated text files (CSV, TSV, pipe-delimited, and\nsimilar). Configure when type is \"csv\".\n\nRequired when type is csv.","properties":{"columnDelimiter":{"type":"string","description":"Character sequence separating fields within each row; comma when omitted. Use \"\\t\"\nfor tab-delimited files; European-locale exports often use semicolons. A wrong\ndelimiter doesn't fail the parse: with a header row, each row becomes one field named\nafter the whole header line (`{\"order_id;sku;qty\": \"1001;TEE-M;2\"}`), so the failure\nshows up in later steps."},"rowDelimiter":{"type":"string","description":"Character sequence marking the end of each record; auto-detected from file content\nwhen omitted. Set explicitly (\"\\n\", \"\\r\\n\", or \"\\r\") only when auto-detection fails,\nsuch as for files with mixed line endings — an incorrect value merges or splits\nrecords."},"hasHeaderRow":{"type":"boolean","description":"When true (the default), the first row is read as field names rather than data, and\nthose names are available in mappings. When false, every row is treated as a data\nrecord and fields are referenced by position."},"trimSpaces":{"type":"boolean","description":"When true, removes leading and trailing whitespace from every field value during\nparsing; when false (the default), whitespace is preserved exactly as in the source.\nHeader row values are always trimmed regardless of this setting, and spaces between\nwords are never affected."},"rowsToSkip":{"type":"integer","description":"Number of rows at the top of the file to ignore before parsing begins — useful for\nreport titles, timestamps, or other metadata above the data. The header row (when\nhasHeaderRow is true) is expected after the skipped rows."},"disableQuoteAndStripEnclosingQuotes":{"type":"boolean","description":"When true, disables CSV quote processing: quotes are treated as literal characters,\nenclosing quotes are stripped, and delimiters inside quoted text split the field.\nWhen false (the default), standard RFC 4180 quoting applies and quoted fields\nprotect embedded delimiters. Enable only for files with non-standard or malformed\nquoting; review this setting first when field counts vary unexpectedly between rows."},"keyColumns":{"type":"array","description":"Field names whose values together identify a record's group when consecutive rows\nshare the same key — used to merge multi-line records (e.g. an order header repeated\nacross line-item rows) into a single record. Leave empty for one record per row; the\ncolumns must exist in the parsed header.","items":{"type":"string"}}}},"json":{"type":"object","description":"Parsing settings for JSON files. Configure when type is \"json\". resourcePath locates the\narray of records when they are nested inside a container object; malformed JSON fails\nthe entire file's processing.\n\nRequired when type is json.","properties":{"resourcePath":{"type":"string","description":"Dot-notation path to the array of records within the JSON structure (e.g.\n\"response.data.customers\"); leave empty when the file's root is already the record\narray. The path must resolve to an array of objects — array indexing and wildcards\nare not supported. A path that resolves to nothing produces zero records without\nraising an error."}}},"xlsx":{"type":"object","description":"Parsing settings for Microsoft Excel (.xlsx) workbooks. Configure when type is \"xlsx\".\nCalculated cell values are extracted rather than formulas; legacy .xls files are not\nsupported — use the modern Open XML format.\n\nRequired when type is xlsx.","properties":{"hasHeaderRow":{"type":"boolean","description":"When true (the default), the first row is read as field names rather than data;\nblank header cells are auto-named and duplicate names are made unique with suffixes.\nWhen false, every row is treated as data and fields receive generic positional names\n(Column1, Column2, ...)."}}},"xml":{"type":"object","description":"Parsing settings for XML documents. Configure when type is \"xml\". resourcePath is an\nXPath expression selecting the elements treated as records; namespaces are handled\nautomatically.\n\nRequired when type is xml.","properties":{"resourcePath":{"type":"string","description":"XPath expression selecting the elements treated as records — each matching element\nbecomes one record, with its child elements as fields. Required for XML parsing;\nthere is no default. Use an absolute path (/Root/Order) when the structure is fixed,\n//Element to match at any depth, or predicates (//Element[@type=\"product\"]) to\nfilter; XPath is case-sensitive, and a non-matching path yields zero records."}}},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"},"fileDefinition":{"type":"object","description":"Parsing via a predefined file definition resource, for formats the standard parsers\ncannot handle — fixed-width files, EDI documents (X12, EDIFACT), and multi-record-type\nor proprietary formats. Configure when type is \"filedefinition\".\n\nRequired when type is filedefinition.","properties":{"_fileDefinitionId":{"type":"string","format":"objectId","description":"File definition resource containing the parsing rules for this format. Must\nreference an existing, accessible file definition — never guess or fabricate the ID.\nDefinitions are reusable across exports, so changing one affects every export that\nuses it."},"allowPartialSuccess":{"type":"boolean","description":"When true, a file whose records partially fail parsing against the file definition\nstill emits the records that did parse, instead of failing the entire file. Applies to\nfile-definition (fixed-width, EDI) parsing only. For X12 and EDIFACT the unit is the\ntransaction set (ST–SE, UNH–UNT), and it takes effect only with an EDI profile linked:\na failed set is set aside and reported as an error while the file's other sets go on\nas records. Without this flag, one failed set fails the whole file."}}},"filter":{"description":"Selects which files are processed: files matching the filter criteria are exported,\nand non-matching files are skipped entirely before processing begins. Filterable\nfields come from each file's listing entry: the name as `fileName`, the size as\n`filesize`, and the modified time as `lastModifiedTime` on FTP, SFTP and S3 or\n`modifiedTime` on cloud-storage folders (`http.type: \"file\"`)\n(e.g. [\"endswith\", [\"string\", [\"extract\", \"fileName\"]], \".csv\"]). There is no\n`lastmodified` key.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"backupPath":{"type":"string","description":"Path where backup copies of source files are stored before processing."},"includeSubdirectories":{"type":"boolean","default":false,"description":"When true, an Azure Data Lake Storage Gen2 transfer (`assistant: azuredatalakestoragegen2`,\n`http.type: file`) also picks up files in the subdirectories below the directory path in\n`http.relativeURI`; when false or omitted only that directory's own files are transferred."},"directory":{"type":"object","required":["pathMode"],"description":"Structured source location for cloud file-provider sources (Google Drive shared drives,\nBox, Dropbox, Google Cloud Storage, Azure Data Lake Storage Gen2), replacing the flat path\nused by classic FTP/S3 sources. Select the location by folder ID or by a path relative to\na configured storage root via pathMode. For the drive-style providers, directoryId mode\naddresses the folder by id alone — the flat relative-path field is not consulted at\nruntime. Google Cloud Storage puts the bucket in `storageRootId` and the object prefix in\n`id` with `pathMode: directoryId`; Azure Data Lake Storage Gen2 puts the container in\n`name` and the directory path in `http.relativeURI`.","properties":{"pathMode":{"type":"string","enum":["relativePath","directoryId"],"description":"How the directory is addressed — by provider folder ID, or by a path relative to a configured storage root."},"id":{"type":"string","description":"Provider-native folder ID of the source directory (e.g. a Google Drive folder ID); set\nwhen pathMode is directoryId. This is the file provider's own ID, not a Celigo resource ID.\nFor Google Cloud Storage it is the object prefix inside the bucket, ending with `/`."},"name":{"type":"string","description":"Display name of the folder identified by id, kept for readability in the UI; the\nfolder browser fills it when the location is picked visually. For Azure Data Lake\nStorage Gen2 it is the authoritative container name instead — the form saves the\ncontainer here and no id."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root (e.g. a Google Drive shared-drive ID) the relative\npath resolves against; set when pathMode is relativePath. The provider's own ID, not a Celigo ID.\nFor Google Cloud Storage it is the bucket name."},"storageRootName":{"type":"string","description":"Display name of the storage root identified by storageRootId, retained for reference in the UI."}}},"backupDirectory":{"type":"object","required":["pathMode"],"description":"Structured destination for backup copies of source files on cloud file providers — the\nfile-provider counterpart to backupPath. Addressed the same way as directory.","properties":{"pathMode":{"type":"string","enum":["relativePath","directoryId"],"description":"How the backup location is addressed — by provider folder ID, or by a path relative to a configured storage root."},"id":{"type":"string","description":"Provider-native folder ID of the backup directory; set when pathMode is directoryId."},"name":{"type":"string","description":"Display name of the backup folder identified by id, kept for readability in the UI;\nthe folder browser fills it when the location is picked visually."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root the backup relative path resolves against; set when pathMode is relativePath."}}}}},"IncludeParentData":{"type":"boolean","description":"When true and a path to records is configured on the export (`pathToRecords` for MongoDB and DynamoDB, `http.response.resourcePath` for HTTP, `file.json`/`file.xml` `resourcePath` for file-based exports), the data outside that path is attached to each extracted record as a `_PARENT` object, keeping the surrounding envelope available to downstream steps. When omitted or false, records are emitted without `_PARENT`, exactly as before. Constraints: on HTTP exports this option cannot be combined with `useXpathParser: true` (rejected at save with `EXPORT_INCLUDE_PARENT_DATA_XPATH_UNSUPPORTED`), and XML responses must resolve to the default converted-JSON parse mode — a run whose XML parser resolves to DOM/XPath mode (for example, because a connection-level field uses an advanced XPath expression) fails at start with the same error rather than silently omitting `_PARENT`. On file-based exports, it requires `file.type` of `json` with `file.json.resourcePath` configured — XML file envelope capture is not yet supported (the streaming XML parser emits no envelope event; XML support arrives with a parsers-package enhancement) — and any other file configuration is rejected at save with `EXPORT_INCLUDE_PARENT_DATA_UNSUPPORTED_FILE_TYPE`."},"Filter":{"type":"object","description":"Configuration for selectively processing records based on specified criteria. This object enables\nprecise control over which items are included or excluded from processing operations.\n\n**Filter behavior**\n\nWhen configured, the filter is applied before processing begins:\n- Items that match the filter criteria are processed\n- Items that don't match are completely skipped\n- No partial processing is performed\n\nSend the whole object. A rules array sent alone as the `filter` value is not\nstored, and a full update of an export that carries one removes the filter the\nexport had, with no error.\n\n**Implementation approaches**\n\nThere are two distinct filtering mechanisms available:\n\n**Rule-Based Filtering (`type: \"expression\"`)**\n- **Best For**: Common filtering patterns based on standard attributes\n- **Capabilities**: Filter by names, values, dates, numerical ranges, text patterns\n- **Advantages**: Declarative, no coding required, consistent performance\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear, static criteria for selection\n\n**Script-Based Filtering (`type: \"script\"`)**\n- **Best For**: Complex logic, dynamic criteria, or business rules\n- **Capabilities**: Full programmatic control, access to complete metadata\n- **Advantages**: Maximum flexibility, can implement any filtering logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Simple rules aren't sufficient or logic needs to be dynamic\n","properties":{"type":{"type":"string","description":"Determines which filtering mechanism to use. This choice affects which properties\nmust be configured and how filtering logic is implemented.\n\n**Available types**\n\n**Rule-Based Filtering (`\"expression\"`)**\n- **Required Config**: The `expression` object with rule definitions\n- **Behavior**: Evaluates declarative rules against item attributes\n- **Best For**: Common patterns like name matching, date ranges, value limits\n- **Advantages**: Simpler to configure, no custom code required\n\n**Script-Based Filtering (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to determine which items to process\n- **Best For**: Complex conditions, business logic, dynamic criteria\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard filtering needs (name, size, date), use `\"expression\"`\n2. For complex logic or conditions not covered by expressions, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based filtering. This object enables filtering\nitems based on common attributes without requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define filtering rules that\ncan match against item attributes like name, type, value, date, and other properties.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Rules can be combined with AND/OR logic\n- Each rule can check a specific attribute\n- Multiple conditions can be applied (ranges, pattern matching, exact matches)\n\n**Common filter patterns**\n\n1. **Pattern matching**: Regular expressions with `matches`\n2. **Value range filtering**: Numbers between min and max values\n3. **Date range filtering**: Items created/modified within specific time ranges\n4. **Status checking**: Items with specific status values or properties\n\nFor AI agents: Rule-based filtering should be your first choice when the filtering criteria\ncan be expressed in terms of standard attributes. Only use script-based filtering when\nmore complex logic is required.\n","properties":{"version":{"type":"string","description":"Version identifier for the expression format. Currently only version \"1\" is supported.\n\nThis field ensures future compatibility if the expression format evolves. Always set to \"1\"\nfor current implementations.\n","enum":["1"]},"rules":{"type":"array","description":"Expression array defining filter conditions using prefix notation. The first element is the operator,\nfollowed by its operands which may themselves be nested expression arrays.\n\nThe rule expression follows this pattern:\n- First element: Operator name (string)\n- Remaining elements: Operands for that operator (values or nested expressions)\n\n**Expression structure**\n\nFilter expressions use a prefix notation where operators appear before their operands:\n```\n[operator, operand1, operand2, ...]\n```\n\n**Comparison Operators**\n- `\"equals\"`: Exact match (equals)\n- `\"notequals\"`: Not equal to value (not equals)\n- `\"greaterthan\"`: Value is greater than specified value (is greater than)\n- `\"greaterthanequals\"`: Value is greater than or equal to specified value (is greater than or equals)\n- `\"lessthan\"`: Value is less than specified value (is less than)\n- `\"lessthanequals\"`: Value is less than or equal to specified value (is less than or equals)\n- `\"startswith\"`: String starts with specified prefix (starts with)\n- `\"notstartswith\"`: String does not start with specified prefix (does not start with)\n- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"notendswith\"`: String does not end with specified suffix (does not end with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"empty\"`: Field is missing or empty (is empty)\n- `\"notempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches a regular expression, ignoring case (matches)\n\nOperator names are case-insensitive: `NOTEMPTY` and `endsWith` work and are stored\nas sent. `isempty`, `isnotempty`, `anyof`, `notcontains`, `doesnotstartwith`,\n`doesnotendwith`, `notmatches`, `doesnotmatch` and `notequal` are not operators:\nthey are refused with 422 \"Invalid operation: <name>\", with the name in lowercase,\nboth when the filter is saved and by `POST /v1/processors/filter`. To negate a\ncondition, wrap it in `not`.\n\nThe string comparisons (`equals`, `contains`, `startswith`, `endswith` and their\nnegations) are case-sensitive; wrap the field in `lowercase` to compare without case.\n`matches` is not: every pattern is compiled with the flags `gi`, so `^paid$` also\nmatches `PAID` and `[a-z]` matches capital letters. An inline flag such as `(?i)`\nmakes the pattern invalid. The filter still saves, but in a run every record fails\nwith `FILTER_EVALUATION_FAILED` (\"Invalid regular expression: /(?i)…/gi: Invalid\ngroup\"), and `POST /v1/processors/filter` refuses the call with 422 (live-verified\n2026-10-22).\n\n**Logical Operators**\n- `\"and\"`: All conditions must be true\n- `\"or\"`: At least one condition must be true\n- `\"not\"`: Negates the condition\n\n**Field Access and Type Conversion**\n- `\"extract\"`: Access a field from the item by name\n- `\"settings\"`: Access a custom setting from the flow, flow step, or integration configuration\n- `\"boolean\"`: Convert value to Boolean type\n- `\"epochtime\"`: Convert value to Epoch Time (Unix timestamp)\n- `\"number\"`: Convert value to Number type\n- `\"string\"`: Convert value to String type\n\n**Field Access Details**\n\n**Using `extract` to access record fields:**\n- Retrieves values from the current record being processed\n- Can access nested properties using dot notation (e.g., `\"customer.email\"`)\n- Paths are relative to the record itself: `[\"extract\", \"record.status\"]`\n  matches nothing\n- In a record filter, must be wrapped in a type conversion (`string`, `number`,\n  `boolean` or `epochtime`): a bare `[\"extract\", ...]` operand is refused with 422\n  \"Field operation extract must be called by a Type Conversion Operation but was called\n  by <operator>\", both when an import's filter is saved and by\n  `POST /v1/processors/filter`\n\n**Using `settings` to access configuration values:**\n- Retrieves values from the integration's configuration settings\n- Supports different scopes with prefix notation:\n  - `flow.settingName`: Access flow-level settings\n  - `flowGrouping.settingName`: Access the flow group's settings\n  - `export.settingName`: Access the export's (or lookup's) own settings\n  - `import.settingName`: Access the import's own settings\n  - `integration.settingName`: Access integration-level settings\n  - `connection.settingName`, `iClient.settingName`: Access the step's\n    connection and iClient settings\n- A scope whose resource has no settings can be absent; a rule that reads\n  it matches nothing\n- Must be wrapped in a type conversion like `extract`: a bare\n  `[\"settings\", ...]` operand is refused with 422 \"Field operation settings\n  must be called by a Type Conversion Operation\"\n- Useful for dynamic filtering based on configuration\n\n**Field Transformations**\n- `\"lowercase\"`: Convert string to lowercase\n- `\"uppercase\"`: Convert string to uppercase\n- `\"ceiling\"`: Round number up to the nearest integer\n- `\"floor\"`: Round number down to the nearest integer\n- `\"abs\"`: Get absolute value of a number\n\n**Arithmetic** (number operands)\n- `\"add\"`, `\"subtract\"`, `\"multiply\"`, `\"divide\"`, `\"modulo\"`: for example\n  `[\"subtract\", [\"number\", [\"extract\", \"total\"]], [\"number\", [\"extract\", \"discount\"]]]`.\n  `mod` is not an operator (422 \"Invalid operation: mod.\").\n\n**Dates**\n- `epochtime` reads ISO 8601 strings and epoch-millisecond numbers. An empty\n  string, a string that isn't a date, or epoch milliseconds sent as a string\n  fail it with \"Invalid date. Cannot convert given string or number to date\n  time.\", followed by `; record=` and the record as JSON: in a run that record fails (`FILTER_EVALUATION_FAILED`, source\n  `output_filter` on an export), and `POST /v1/processors/filter` and an\n  export preview refuse the whole call with 422. `and` and `or` evaluate\n  every operand, so a `notempty` test beside it doesn't prevent the failure.\n  A missing or null field doesn't match.\n- `datetime`, `date` and `timestamp` are not type conversions: 422\n  \"Invalid operation: <name>.\"\n- `number` reads a missing or empty field as 0.\n\n**Handlebars operands**\n- An operand can also be a string holding exactly one Handlebars expression, such as\n  `\"{{record.status}}\"` or `\"{{lowercase record.code}}\"`, on either side of the\n  operator. It renders against the record (`record.<field>`; an unprefixed\n  `{{status}}` renders empty) before the comparison.\n- The rendered value is a string: wrap it in `[\"number\", ...]` to compare it with a\n  number, or the rule is refused with 422.\n- `epochtime` does not render Handlebars: `[\"epochtime\", \"{{timestamp}}\"]` is refused\n  with 422 \"invalid datetime format\".\n- A string with text around the expression, or with more than one expression, is\n  compared as a literal.\n- `[\"number\", \"{{timestamp \\\"x\\\"}}\"]` is the current time in epoch milliseconds,\n  so `[\"greaterthanequals\", [\"epochtime\", [\"extract\", \"created\"]], [\"subtract\",\n  [\"number\", \"{{timestamp \\\"x\\\"}}\"], 604800000]]` keeps records created in the\n  last 7 days.\n- An unknown helper fails the rule with 422 \"Failed to evaluate handlebars\n  expression\".\n\nChoose the type conversion that matches the comparison — `number` for numeric\ncomparisons, `epochtime` for dates, `string` for text. For example:\n\n```json\n[\n  \"equals\",\n  [\n    \"number\",  // Convert to number before comparison\n    [\n      \"extract\",\n      \"quantity\"\n    ]\n  ],\n  100\n]\n```\n\nExample with datetime conversion:\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",  // Convert to Unix timestamp before comparison\n    [\n      \"extract\",\n      \"createdDate\"\n    ]\n  ],\n  1609459200000  // January 1, 2021 as Unix timestamp in milliseconds\n]\n```\n\nExample with transformations:\n```json\n[\n  \"and\",\n  [\n    \"matches\",\n    [\n      \"lowercase\",  // Convert to lowercase before matching\n      [\n        \"string\",\n        [\n          \"extract\",\n          \"categories\"\n        ]\n      ]\n    ],\n    \"netsuite\"\n  ],\n  [\n    \"notequals\",\n    [\n      \"string\",\n      [\n        \"extract\",\n        \"recurrence.pattern.type\"\n      ]\n    ],\n    \"\"\n  ]\n]\n```\n\nExample comparing a record field with a flow setting:\n```json\n[\n  \"equals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"trantype\"\n    ]\n  ],\n  [\n    \"string\",\n    [\n      \"settings\",\n      \"flow.trantype\"\n    ]\n  ]\n]\n```\n\n**Examples**\n\nExample 1: Status field is not equal to \"cancelled\"\n```json\n[\n  \"notequals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"status\"\n    ]\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"filename\"\n    ]\n  ],\n  \"HC\"\n]\n```\n\nExample 3: Amount is greater than 100\n```json\n[\n  \"greaterthan\",\n  [\n    \"number\",\n    [\n      \"extract\",\n      \"amount\"\n    ]\n  ],\n  100\n]\n```\n\nExample 4: Order date is after January 1, 2023 (UTC). Compare dates as `epochtime`\nagainst epoch milliseconds: a `string` comparison of ISO timestamps orders values with\ndifferent UTC offsets wrongly.\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",\n    [\n      \"extract\",\n      \"orderDate\"\n    ]\n  ],\n  1672531200000\n]\n```\n","items":{"oneOf":[{"title":"String","type":"string"},{"title":"Number","type":"number"},{"title":"Boolean","type":"boolean"},{"title":"Object","type":"object"},{"title":"Array","type":"array"},{"title":"Null","type":"null"}]}}}},"script":{"type":"object","description":"Configuration for programmable script-based filtering. This object enables complex, custom\nfiltering logic beyond what expression-based filtering can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to determine which items\nshould be processed.\n\n**Implementation approach**\n\nScript-based filtering works by:\n1. Executing the specified function from the referenced script\n2. Passing item data to the function\n3. Using the function's return value (true/false) to determine inclusion\n\n**Common use cases**\n\nScript filtering is ideal for:\n- Complex business logic that can't be expressed as simple rules\n- Dynamic filtering criteria that change based on external factors\n- Content-based filtering that requires deep inspection\n- Advanced pattern matching beyond simple wildcards\n- Multi-stage filtering with intermediate logic\n\nFor AI agents: Only use script-based filtering when expression-based filtering is insufficient.\nScript filtering requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to the Script resource that contains the filtering logic. This must be a valid\nObjectId of a Script resource in this account: any other id fails the save with 422\n(\"Only a script owned by the user or a script shared by the Integration App can be used\nat filter.script._scriptId.\").\n\nThe referenced script must contain the function specified in the `function` field\nand must be written to handle filtering specifically. The script receives\nitem data as its input and must return a boolean value indicating whether\nto process the item (true) or skip it (false).\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n"},"function":{"type":"string","description":"Name of the function within the script to execute for filtering decisions. This function\nmust exist in the script referenced by _scriptId.\n\n**Function requirements**\n\nThe specified function must:\n- Accept item data as its first parameter\n- Return a boolean value (true to process the item, false to skip it)\n- Handle errors gracefully\n- Execute efficiently (as it may run for many items)\n\n**Function signature**\n\n```javascript\nfunction filterItems(itemData) {\n  // itemData contains properties of the item being evaluated\n  // Custom logic here\n  return true; // or false to skip the item\n}\n```\n\nFor AI agents: Ensure the function name exactly matches a function defined in the\nreferenced script, as mismatches will cause the filter to fail.\n"}}}}},"Distributed":{"type":"object","description":"Authentication settings for distributed (real-time listener) exports.","properties":{"bearerToken":{"type":"string","description":"Bearer token that a system pushing data into this export sends as\n`Authorization: Bearer <token>`. Optional — most distributed listeners (NetSuite,\nSalesforce real-time) use no token. Write-only and stored encrypted; treat it as a\nsensitive credential.","format":"password"}}},"Cdc":{"type":"object","description":"Change-data-capture configuration for real-time listener exports (the \"Listen for real-time\ndata\" source step). Present only when `type` is `stream`. Set on `MongodbExport` (MongoDB change\nstreams) and `RDBMSExport` (SQL Server CDC and PostgreSQL logical replication); the listener\nstreams change events continuously instead of fetching records on a schedule.\n\nOn save the API normalizes the `properties` grid into the typed fields: a `snapshot.mode`\nentry populates `snapshotMode` and a `capture.mode` entry populates `captureMode`. Every\nstream export must resolve a snapshot mode from one of those two places.","properties":{"captureMode":{"type":"string","enum":["change_streams_update_full","change_streams_update_full_with_pre_image"],"default":"change_streams_update_full","description":"How MongoDB change-stream events are materialized into records. Use\n`change_streams_update_full_with_pre_image` when downstream steps need the document's prior\nstate. MongoDB listeners only — `RDBMSExport` (SQL Server CDC, PostgreSQL) does not set this."},"snapshotMode":{"type":"string","enum":["initial","no_data","when_needed","initial_only"],"default":"no_data","description":"Whether the listener seeds existing rows before streaming changes. Use `initial` to backfill\nexisting data on the first run; `no_data` to stream only new changes."},"slotName":{"type":"string","pattern":"^[a-z_][a-z0-9_]{0,62}$","description":"Name of the PostgreSQL logical replication slot the listener consumes. PostgreSQL listeners\nonly. When omitted, the platform manages the slot name. Must be a valid PostgreSQL\nidentifier (lowercase letters, numbers, or underscores, starting with a letter or\nunderscore)."},"publicationName":{"type":"string","pattern":"^[a-z_][a-z0-9_]{0,62}$","description":"Name of the PostgreSQL publication that defines which tables stream changes. PostgreSQL\nlisteners only. When set, the publication is the sole table filter and `rdbms.tables`\nstays unset. Must be a valid PostgreSQL identifier."},"cursor":{"type":"string","description":"Server-managed stream position marker (where the listener resumes reading change events).\nMaintained by the platform as events are consumed; the UI exposes a reset workflow for it\nunder Cursor Management."},"paths":{"type":"array","items":{"type":"string"},"description":"Dot-notation paths within each raw change event to retain in the emitted record. MongoDB\nlisteners default to `[\"payload\"]` (the whole change document); SQL Server and PostgreSQL\nlisteners commonly select `payload.after`, `payload.op`, and related fields to expose the\nchanged row and operation type."},"intervalTime":{"type":"integer","default":300,"description":"Polling interval, in seconds, at which the listener checks the source for new change events."},"properties":{"type":"array","description":"Connector-engine settings passed through to the underlying CDC connector as name/value\npairs. Mirrors the form's additional-properties grid: the required keys (`capture.mode`\nand `snapshot.mode` for MongoDB, `snapshot.mode` for SQL Server and PostgreSQL) are\nserialized first, followed by optional keys such as `field.exclude.list`,\n`column.include.list`, or `snapshot.collection.filter.overrides.<db>.<collection>`.\nOn save the API also lifts `snapshot.mode` / `capture.mode` entries into the typed\n`snapshotMode` / `captureMode` fields.","items":{"type":"object","required":["name","value"],"properties":{"name":{"type":"string","description":"Connector property key (e.g. `capture.mode`, `snapshot.mode`, `field.exclude.list`)."},"value":{"type":"string","description":"Connector property value."}}}}}},"Topic":{"type":"object","description":"Subscription settings for an Event Streams listener (`adaptorType: TopicExport`). The\nlistener consumes messages published to the topic and hands them to the flow in batches;\nthe platform delivers them internally, so the export carries no `_connectionId`. Present\nwhen `adaptorType` is `TopicExport`; the server fills `startPosition` and `maxWaitTimeMs`\nwith their defaults when they are omitted.","properties":{"_topicId":{"type":"string","format":"objectId","description":"Topic this listener subscribes to. Create or look it up with `GET /v1/topics`; the\ntopic must belong to the same account. Pointing an existing listener at a different\ntopic starts a new subscription, read from `startPosition`. A clone made in the same\nenvironment — of this export, or of a flow that contains it — keeps `_topicId`, so the\ncopy becomes another listener on the topic once its flow is enabled."},"startPosition":{"type":"string","enum":["latest","earliest"],"default":"latest","description":"Where the listener starts reading when it first subscribes to `_topicId`. Use `earliest`\nto backfill retained history, `latest` (the default) to process only new messages. Read\non create and whenever `_topicId` changes; on other updates the stored value is kept even\nif a different value is sent."},"maxWaitTimeMs":{"type":"integer","minimum":1000,"default":30000,"description":"Longest time, in milliseconds, the listener waits to fill a batch before delivering\nwhatever it has collected. Lower values reduce latency for sparse topics; the default\nof 30 seconds favors fuller batches. The batch record cap is the export's `pageSize`."}}},"FileSystem":{"type":"object","description":"Defines which files to read from a local or mounted folder on the host running the\non-premise agent. Required when the _connectionId field references a file-system (on-premise)\nconnection; must not be included for other connection types.","required":["directoryPath"],"properties":{"directoryPath":{"type":"string","description":"Folder on the on-premise agent's host to read files from; the agent's OS account must\nhave read permission on it. Accepts a local OS path or a UNC network share, and supports\nhandlebars templates for dynamic folders."}}},"Http":{"type":"object","description":"Configuration for HTTP exports. Required when the export's `_connectionId` references a\nconnection of type `http`, and on `NetSuiteHTTPExport` (NetSuite's REST APIs on a `netsuite`\nconnection, where `nsDomainType` supplies the host).","properties":{"type":{"type":"string","enum":["file","blob","csvstream"],"description":"Set to `file` when the export works with files rather than a record-returning endpoint: raw file downloads (PDFs, images, binary data) and the folder mode of the cloud file providers (Google Drive, Box, Dropbox, Azure Blob, Google Cloud Storage, Celigo Storage), where each file in the folder is parsed by the `file` object's `type` (csv, json, xml, xlsx, filedefinition — including EDI) into records; the `file` object must also be configured. Leave undefined for standard data exports that return structured records (JSON, XML, GraphQL, SOAP responses) — the response is then parsed into records for downstream steps. When set to `file` or `blob`, the export appears as a \"Transfer\" step in Flow Builder instead of a standard \"Export\" step."},"formType":{"type":"string","enum":["http","rest","graph_ql","assistant","assistant_graphql"],"description":"Authoring mode of the HTTP request form. Controls which editor the UI presents\n(standard HTTP, legacy REST, GraphQL, or connector-assistant driven) and how the\nrequest fields are interpreted. When omitted, the standard HTTP form is used."},"method":{"type":"string","description":"HTTP method used to request data from the target API. Consult the target API's\ndocumentation to determine the appropriate method. `HEAD` exists for connector endpoints\nthat return metadata in response headers only (Azure Data Lake Storage Gen2 path\nproperties).","enum":["GET","POST","PUT","PATCH","DELETE","HEAD"]},"followRedirects":{"type":"boolean","default":true,"description":"When explicitly false, 3xx responses are not followed — the redirect\nresponse itself (status code, `Location` header, body) becomes the\nrecord. Omitted or true follows allowed redirects (the default\nbehavior)."},"maxRedirects":{"type":"integer","minimum":1,"maximum":10,"description":"Caps how many consecutive 3xx redirects are followed. Only applies\nwhen `followRedirects` is not false; to not follow at all, set\n`followRedirects: false` rather than `maxRedirects: 0`. Decimal\nvalues are rejected on save."},"relativeURI":{"type":"string","description":"Resource path appended to the connection's `baseURI` to form the complete request URL. Path segments, query parameters, or the entire value can be built with handlebars expressions for endpoints determined at runtime. For lookup exports (`isLookup: true`) with mappings configured, handlebars always render against the original pre-mapped input record, so fields removed or renamed by mappings remain available for URI construction. On `NetSuiteHTTPExport` the base URL is derived from the NetSuite connection and `nsDomainType`, so the path starts at `/services/rest/...` or `/app/site/hosting/restlet.nl`.\n\nLeave it empty to call the connection's `baseURI` exactly; `/` appends a trailing slash, which is a different URL. An export saved with an empty relative URI and no `http.body` comes back from reads without its `http` block, so a PUT built from such a read loses the stored settings: send the complete `http` block with every PUT."},"headers":{"type":"array","description":"Headers specific to this export, merged with (and able to override) headers defined on the connection. Define common headers such as authentication on the connection instead. Values support handlebars expressions; for lookup exports with mappings, values render against the pre-mapped input record.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header to send; a header defined here overrides a same-named header from the connection."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions; for lookup exports with mappings, the value renders against the pre-mapped input record."}}}},"requestMediaType":{"type":"string","description":"Overrides the connection-level request media type for this export. Set only when this endpoint requires a different format than the connection default.","enum":["json","xml","urlencoded","form-data","plaintext"]},"body":{"type":"string","description":"Request body sent with POST, PUT, or PATCH requests, typically carrying query or filter criteria for APIs (such as GraphQL or SOAP) that expect them in the body. The content must match the format set by `requestMediaType` and supports handlebars expressions for dynamic values."},"successMediaType":{"type":"string","description":"Media type used to parse successful response bodies. Set only when the response format differs from the request format.","enum":["json","xml","csv","plaintext"]},"errorMediaType":{"type":"string","description":"Media type used to parse error response bodies. Set only when error responses use a different format than the request.","enum":["json","xml","plaintext"]},"_asyncHelperId":{"type":"string","format":"objectId","description":"AsyncHelper resource that handles polling for long-running operations on APIs that process requests asynchronously (HTTP 202 responses, job tickets, feed or document IDs). Set when the export must submit a request, poll for status, and retrieve results once the external process completes — for example Amazon SP-API feeds or large report generators."},"once":{"type":"object","description":"Callback configuration for once exports, used to mark records as exported in the source system after successful processing.\nCalled only while the export's `type` is `once`: an export with no `type` keeps a saved `once` and never calls it, so every\nrun reads every record again (live-verified 2026-10-22).","properties":{"relativeURI":{"type":"string","description":"Relative path (starting with `/`) called on the source system to mark each record as exported after successful processing. Supports handlebars variables and renders against the pre-mapped record — mappings do not apply to the callback URI."},"method":{"type":"string","description":"HTTP method used for the mark-as-exported callback request.","enum":["GET","PUT","POST","PATCH","DELETE"]},"body":{"type":"string","description":"Request body sent with the mark-as-exported callback. Supports handlebars expressions for dynamic values."}}},"paging":{"type":"object","description":"Controls how the export requests subsequent pages when the API returns multi-page responses. The `method` field determines which companion fields are required and how each next page is requested. For the page, skip, and token methods, reference the matching pagination variable (`{{export.http.paging.page}}`, `{{export.http.paging.skip}}`, or `{{export.http.paging.token}}`) in the relative URI or request body — misconfigured pagination is a common cause of incomplete data retrieval.\n\nWhen this export is the first lookup of a builder-mode API with `pagination.enabled` set, it is that API's volume driver: saving the export (even unchanged) invalidates the API's issued cursors, so a caller mid-walk gets an empty final page, and removing `paging` is accepted on save but makes every call to the API fail with `400 api_pagination_source_not_paged` until paging is restored or the API's flag is turned off.\n\nWhen a next page's response is identical to the previous one, paging stops: the export records an open error with code `page_response_error` (\"Identical response received from next page request. No more page requests will be made.\") and the run completes with the pages already read, so later pages are never exported. A request that doesn't change between pages produces this, as observed with `method: page` and the pagination variable missing from the relative URI and body; reference the variable so each request asks for the next page.","properties":{"method":{"type":"string","description":"Pagination strategy used to request each subsequent page; match it to the mechanism documented by the target API. Determines which companion fields are required: `token` needs `path` and `pathLocation`, `url` needs `path`, `relativeuri` needs `relativeURI`, `body` needs `body`, while `linkheader` typically needs no extra configuration. Using the wrong method results in errors or incomplete data retrieval.","enum":["linkheader","page","skip","token","url","relativeuri","body"]},"page":{"type":"integer","description":"Starting page number for `method: page`. Set to 1 for APIs whose first page is not zero-indexed; when omitted, paging starts at 0. The value is incremented automatically for each subsequent page request."},"skip":{"type":"integer","description":"Starting offset for `method: skip`. Rarely needed — most APIs start at 0, and the value is incremented by the page size automatically for each subsequent request. Set only when the API requires a non-zero starting offset."},"token":{"type":"string","description":"Initial token for `method: token`. Leave empty for normal pagination — the first request is sent without a token, and subsequent tokens are extracted from each response via `path`. Set only to resume from a known token or for APIs that require a token on the first request."},"path":{"type":"string","description":"Location of the pagination value in each response — the continuation token for `method: token`, or the complete next-page URL for `method: url`. When `pathLocation` is `body`, use a dot-notation JSON path (e.g. `meta.nextToken`); when `header`, use the exact case-sensitive header name. Not used by other pagination methods."},"pathLocation":{"type":"string","description":"Where the export looks for the pagination token referenced by `path`. Required for `method: token`; not used by other pagination methods.","enum":["body","header"]},"pathAfterFirstRequest":{"type":"string","description":"Alternative token location used for responses after the first page, in the same format as `path`. Set only when the API moves the token to a different location in subsequent responses — setting it unnecessarily can cause pagination to fail."},"relativeURI":{"type":"string","description":"Overrides the main relative URI for second and later page requests; leave empty when the main relative URI works for all pages. Build it with handlebars using the `previous_page` context — `previous_page.full_response` (the prior response body), `previous_page.last_record` (the last record of the prior page), and `previous_page.headers` (the prior response headers)."},"body":{"type":"string","description":"Overrides the main request body for second and later page requests, typically for GraphQL or SOAP APIs that paginate through the body; leave empty when the main body works for all pages. Build it with handlebars using the `previous_page` context — `previous_page.full_response`, `previous_page.last_record`, and `previous_page.headers`."},"mergeBodyParamsToPagingBody":{"type":"boolean","description":"Only applies when `paging.method` is `body`. When true, the body\nparameters produced by the export's mappings at runtime are merged\ninto the evaluated `paging.body` for page 2+ requests, so paging\nrequests keep the same mapped parameters as the first page.\nOmitted/false keeps the original behavior — page 2+ requests send\nonly the evaluated `paging.body`."},"linkHeaderRelation":{"type":"string","description":"Link header relation followed for `method: linkheader` when the API uses a value other than the default `next`. Case-sensitive and must exactly match the `rel` value in the Link header, without the `rel=` prefix."},"resourcePath":{"type":"string","description":"Overrides the path to records for second and later page responses. Set only when follow-up pages place records at a different location than the first response; leave empty when all pages share the same structure."},"lastPageStatusCode":{"type":"integer","description":"HTTP status code that signals the last page, replacing the default behavior of treating 404 as the end of pagination. When this status is received (any code but `200` paired with `lastPagePath`), paging stops and the response is not treated as an error. Set it when the API uses a non-404 code (such as 204 or 400) to indicate no more pages, or to `200` together with `lastPagePath` when the last page is an ordinary 200 response: then the response is parsed, its records are kept, and paging stops only when `lastPagePath` matches `lastPageValues`."},"lastPagePath":{"type":"string","description":"JSON path to a response-body field that signals the end of pagination, such as a \"has more\" flag or a cursor that empties on the last page. Must be used with `lastPageValues`, which lists the values at this path that stop paging. If the path does not exist in a response, the condition is not considered met. With `method: token` the path is read only on a response whose status code equals `lastPageStatusCode`, so pair it with `lastPageStatusCode: 200` for an ordinary last page; with `method: body` it is read on every response."},"lastPageValues":{"type":"array","description":"Values at `lastPagePath` that stop pagination; a match on any entry ends paging. All entries are compared as exact, case-sensitive strings, even for boolean or numeric fields — use `\"true\"`, `\"false\"`, `\"null\"` for JSON null, or `\"\"` for an empty string. Required when `lastPagePath` is set.","items":{"type":"string"}},"maxPagePath":{"type":"string","description":"JSON path to the total page count in the response, used to stop paging once the last page is reached. Only applies to the `page` and `skip` methods. Point it at the total number of pages, not the current page number."},"maxCountPath":{"type":"string","description":"JSON path to the total record count in the response, used to stop paging once all records have been retrieved. Only applies to the `page` and `skip` methods; when both are set, `maxPagePath` takes precedence. Point it at the total number of records, not the count in the current page."}},"if":{"properties":{"method":{"const":"body"}},"required":["method"]},"then":{"required":["body"]},"else":{"if":{"properties":{"method":{"const":"token"}},"required":["method"]},"then":{"required":["path","pathLocation"]},"else":{"if":{"properties":{"method":{"const":"relativeuri"}},"required":["method"]},"then":{"required":["relativeURI"]},"else":{"if":{"properties":{"method":{"const":"url"}},"required":["method"]},"then":{"required":["path"]}}}}},"response":{"type":"object","description":"Controls how records are extracted from the API response and how success or failure is detected at the response level. When the API wraps records in an envelope object, set `resourcePath` to the path of the records array — without it, the entire response body is treated as a single record. Leave this object undefined when the API returns a bare JSON array.","properties":{"resourcePath":{"type":"string","description":"Dot-separated path to the array of records inside the response body (e.g. `data.customers` for `{\"data\": {\"customers\": [...]}}`); without it, a wrapped response is treated as a single record. Values starting with `$` are evaluated as JSONPath instead of dot notation — including unions (`$['orders','invoices'][*]`) and filter expressions (`$.items[?(@.qty>3)]`); all other values keep the existing dot-notation behavior unchanged. Leave undefined when the API returns a bare JSON array. This extracts records from the API response — not to be confused with `oneToMany`/`pathToMany`, which unwrap arrays from input records, or `paging.resourcePath`, which applies only to subsequent page responses."},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"},"resourceIdPath":{"type":"string","description":"Path to the unique identifier within each record, used primarily when processing results of asynchronous import responses. When omitted, the system looks for standard `id` or `_id` fields automatically."},"successPath":{"type":"string","description":"Path to a response field that signals whether the call succeeded, for APIs that return HTTP 200 even on failure. With `successValues`, the value at this path must match one of them; without `successValues`, the path only has to resolve — a response in which it is absent fails with `response_failure` (\"Unable to extract paths from response body using path: <path>, Response: …\")."},"successValues":{"type":"array","items":{"type":"string"},"description":"Values at `successPath` that mark the response as successful; any other value is treated as an error. All comparisons are string-based — use `\"true\"` or `\"false\"` for boolean fields."},"errorPath":{"type":"string","description":"Path to the error message in the response body. The value at this path is included in error logs and error records when the API returns an error; without it, the message is the whole response body."},"failPath":{"type":"string","description":"Path to a response field that signals failure even when the HTTP status code is 200 — the inverse of `successPath`. With `failValues`, the response fails when the value there matches one of them; without, when the path holds a truthy value. A path that resolves to an array matches only when the array has exactly one element."},"failValues":{"type":"array","items":{"type":"string"},"description":"Values at `failPath` that mark the response as failed, even when the HTTP status code is 200."},"allowArrayforSuccessPath":{"type":"boolean","description":"When true, treats the value at `successPath` as an array and counts the response as successful if any element matches `successValues`, rather than requiring a single scalar match. Set it for APIs that return per-record status arrays in a batch response."},"twoDArray":{"type":"object","description":"Parsing options for endpoints that return tabular data as a two-dimensional array (rows of cells) rather than an array of objects, such as spreadsheet-style or report APIs.","properties":{"hasHeader":{"type":"boolean","description":"When true, the first row is treated as column headers and used to name the fields of each generated record."},"doNotNormalize":{"type":"boolean","description":"When true, the rows are passed through as raw arrays instead of being normalized into keyed records."}}},"blobFormat":{"type":"string","description":"Controls how the binary response body is decoded for blob exports. Only relevant when `http.type` is `file` or the export type is `blob`.","enum":["utf8","ucs2","utf-16le","ascii","binary","base64","hex"]}}},"_httpConnectorVersionId":{"type":"string","format":"objectId","description":"Pins the export to a version of the published HTTP connector its connection is bound to\n(`connection.http._httpConnectorId`), so the UI renders that connector's endpoint form instead\nof a hand-authored request. Linked on save together with `_httpConnectorResourceId` and\n`_httpConnectorEndpointId` — see that field for the rule."},"_httpConnectorResourceId":{"type":"string","format":"objectId","description":"The connector resource (a group of related endpoints, such as Orders) that this export's\nendpoint belongs to. Linked on save together with `_httpConnectorEndpointId` — see that field\nfor the rule."},"sendAuthForFileDownloads":{"type":"boolean","description":"When true, includes authentication headers when downloading files."},"_httpConnectorEndpointId":{"type":"string","format":"objectId","description":"The connector endpoint this export calls, shown in the UI as the selected endpoint. On save the\nplatform derives it (with the version and resource ids) from `relativeURI` and `method` when\nthey match an endpoint of the connector the connection is bound to — path parameters, literal\nids and query strings still match — and replaces ids that belong to another connector. An id for\nthe connection's own connector is kept as sent even when the URI names a different endpoint;\nwhen nothing matches, or the connection has no connector, whatever was sent is kept and nothing\nis derived."}}},"Salesforce":{"type":"object","description":"Configuration for Salesforce exports. Required when the export's `_connectionId` references a Salesforce connection; omit for other connection types. The `type` field selects the extraction mode: `soql` runs batch queries and requires the `soql` object, while `distributed` listens for real-time events and requires the `distributed` object. File retrieval (when the export's `type` is `blob`) requires `sObjectType` and `id` instead.","properties":{"type":{"type":"string","description":"Selects the extraction mode and determines which configuration object is required. Use `soql` for scheduled or on-demand batch queries and lookups — it requires the `soql` object and works with both the `rest` and `bulk` APIs and the standard, delta, test, and once export types. Use `distributed` for real-time event handling — it requires the `distributed` object, always appears as a Listener in Flow Builder, ignores the `api` field, and supports only the standard export type.","enum":["soql","distributed"]},"sObjectType":{"type":"string","description":"API name of the Salesforce object the export operates on; the object must exist in the connected org and be accessible to the integration user. Required for distributed exports and for blob exports — blob exports accept only the file storage objects (`Attachment`, `ContentVersion`, `Document`) paired with the `id` field. Optional for SOQL exports, where the object can be inferred from the query."},"id":{"type":"string","description":"Salesforce record ID of the file to retrieve; required for blob exports and not used for `soql` or `distributed` exports. Accepts a static ID or a handlebars expression such as `{{record.Attachment_Id__c}}` to resolve the file at runtime — dynamic IDs require the export to run as a lookup (`isLookup: true`). The ID must belong to the object named in `sObjectType`."},"includeDeletedRecords":{"type":"boolean","description":"When true, SOQL exports use Salesforce's `queryAll()` instead of `query()`, including Recycle Bin records deleted within the past 15 days; each record's `IsDeleted` field identifies deletions. Useful for synchronizing deletes to target systems or maintaining a complete audit trail. Ignored for distributed and blob exports.","default":false},"api":{"type":"string","description":"Salesforce API used to run SOQL queries; ignored for distributed and blob exports. Use `rest` (the default when omitted) for smaller datasets (under 10,000 records) and for lookup exports — the Bulk API is not compatible with `isLookup: true`. Use `bulk` for large data volumes and higher throughput, optionally tuned through the `bulk` object.","enum":["rest","bulk"]},"bulk":{"type":"object","description":"Settings for Salesforce Bulk API 2.0 jobs. Only applies when `api` is `bulk` on a SOQL export; ignored otherwise.","properties":{"maxRecords":{"type":"integer","description":"Caps how many records a single Bulk API job retrieves, which helps prevent timeouts with complex queries or large records. When omitted, Salesforce's default applies. Lower values suit complex or custom objects; higher values improve throughput for simple records.","minimum":10000},"purgeJobAfterExport":{"type":"boolean","default":true,"description":"When true, deletes the Bulk API job in Salesforce after all data is retrieved, keeping the Bulk Data Load Jobs list clean — at the cost of making job details unavailable for later troubleshooting. Has no effect on the data retrieved or the success of the export."}}},"soql":{"type":"object","description":"SOQL query configuration; required when `type` is `soql` and not used otherwise. The query controls which objects, fields, and filter conditions the export retrieves, with either the REST or Bulk API.\n\nRequired when type is soql.","properties":{"query":{"type":"string","description":"SOQL statement passed directly to Salesforce that defines which objects, fields, and records the export retrieves. Supports handlebars — for delta exports, filter with `WHERE LastModifiedDate > {{lastExportDateTime}}` — and relationship queries that fetch parent and child records together. Select only the fields you need and use `ORDER BY` for consistent results across pages.","maxLength":200000}}},"distributed":{"type":"object","description":"Real-time listener configuration — the object that makes an export a listener. Set it, with `type: distributed`, when creating one; a listener saved from the integrator.io form carries it with no `type`, and the platform fires it either way. Not used on SOQL or file exports. Records reach the flow only when an Apex trigger on `sObjectType` in the connected org calls the Celigo package (`integrator_da__.RealTimeExporter.processExport()`) — no scheduling or manual execution is involved. Integration Apps create their triggers at installation; for a custom flow the customer deploys the trigger in Salesforce, starting from the code the export page shows under Required trigger.","properties":{"referencedFields":{"type":["array","null"],"description":"Fields from related objects to include in the exported record, written in dot notation (e.g. `Account.Name`, `Owner.Email`, `Custom_Lookup__r.Field__c`). Works with lookup and master-detail relationships, up to 10 unique referenced relationships per export. Referenced fields are retrieved via separate API calls, so include only fields the integration actually needs.","items":{"type":["string","null"]}},"disabled":{"type":"boolean","description":"When true, the listener stops processing events while its configuration and Salesforce triggers stay in place. Events that occur while disabled are ignored, not queued — they are not processed retroactively when re-enabled, so consider a delta export to catch up after extended pauses."},"qualifier":{"type":["string","null"],"description":"SOQL WHERE-clause condition that filters which record events the listener processes; when omitted (stored as `null`), every event for the object is processed. The Celigo Salesforce package evaluates it inside Salesforce as `SELECT Id FROM <sObjectType> WHERE (<qualifier>) AND Id IN :ids` before anything is sent, so only fields of the monitored object and SOQL comparison syntax apply — Salesforce formula functions are not part of this grammar. A deleted record is never returned by that query, so a listener with any qualifier receives no delete events; leave it unset when removals must trigger the flow."},"batchSize":{"type":["integer","null"],"description":"Controls how many event records are grouped into each real-time processing batch. Stored as `null` when not configured. Smaller batches lower latency for time-sensitive operations; larger batches improve throughput for high-volume objects. Does not limit how many records are processed in total — only how they are grouped.","minimum":4,"maximum":200},"skipExportFieldId":{"type":["string","null"],"description":"API name of a Salesforce checkbox field used to prevent infinite loops in bidirectional syncs: when the integration updates a record, this field is set so the resulting event is ignored, then cleared automatically. Required for bidirectional sync scenarios. The field must be a checkbox dedicated to integration use and updateable by the integration user."},"relatedLists":{"type":["array","null"],"description":"Child record sets to include with the parent record when it changes, one entry per related list. When omitted (stored as `null`), only the primary record is processed. Each related list adds Salesforce API calls and increases payload size.","items":{"type":"object","description":"Defines how to retrieve one type of child record related to the primary object. Configure multiple entries to retrieve different types of children.","properties":{"referencedFields":{"type":"array","description":"API names of the child object fields to retrieve; only the listed fields are included, and an empty array retrieves only the `Id` field. Include only fields the integration needs — each field adds data volume and processing time.","items":{"type":["string","null"]}},"parentField":{"type":"string","description":"API name of the lookup or master-detail field on the child object that references the parent (e.g. `AccountId`, `Parent_Object__c`) — the field's API name, not the relationship name. Used to build the query that fetches children for each parent record."},"sObjectType":{"type":"string","description":"Case-sensitive API name of the child object to retrieve (e.g. `Contact`, `Custom_Child__c`) — use the API name, not the label. The object must have a relationship field to the parent and be accessible to the connected user."},"filter":{"type":"string","description":"SOQL condition that limits which child records are included; when omitted, all related children are retrieved. Provide only the condition expression without the `WHERE` keyword — it is combined automatically with the parent relationship filter."},"orderBy":{"type":"string","description":"Sort order for the retrieved child records, as field names with optional `ASC`/`DESC` directions (e.g. `CreatedDate DESC`). Provide only the fields and directions without the `ORDER BY` keywords. When omitted, Salesforce determines the order."}},"required":["parentField","referencedFields","sObjectType"]}}}}},"if":{"properties":{"type":{"const":"distributed"}},"required":["type"]},"then":{"required":["distributed","sObjectType"]},"else":{"if":{"required":["distributed"]},"then":{},"else":{"if":{"required":["id"]},"then":{"required":["sObjectType"]},"else":{"if":{"not":{"propertyNames":{"enum":["metadata"]}}},"then":{"required":["soql","type"]}}}}},"AS2":{"type":"object","description":"Configures AS2 exports and listeners. Optional — the transport settings live on the AS2\nconnection, and an AS2 export is valid with only `file` (parse settings); set this object\nto link a Trading Partner Connector or to transfer raw files as blobs. An AS2 listener\nacts as the flow's source, receiving trading-partner transmissions in near real-time and\nhandling decryption, signature verification, and MDN generation; AS2 (Applicability\nStatement 2) transmits EDI and other data securely over HTTP/S using S/MIME encryption\nand digital signatures.","properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies the partner-specific EDI configuration —\ncommunication protocol, document schemas, mappings, validation rules, and endpoint\ndetails. Set this to link the export to all settings required for AS2 communication\nwith that partner."},"blob":{"type":"boolean","description":"When true, retrieves raw files without parsing them into structured records (rendered as\na \"Transfer\" step in the flow UI). Use only when the file contents are not needed in\nsubsequent steps — for binary files or when parsing is handled downstream. Only\navailable on AS2 and VAN exports."}},"required":[]},"DynamoDB":{"type":"object","description":"Defines how records are queried from DynamoDB tables. Required when the _connectionId field\nreferences a DynamoDB connection; must not be included for other connection types. Basic\nexports need region, method, tableName, keyConditionExpression, expressionAttributeNames,\nand expressionAttributeValues; once exports (export type \"once\") additionally need\nonceExportPartitionKey, plus onceExportSortKey for composite-key tables.","required":["region","tableName","keyConditionExpression","expressionAttributeNames","expressionAttributeValues"],"properties":{"region":{"type":"string","enum":["us-east-1","us-east-2","us-west-1","us-west-2","af-south-1","ap-east-1","ap-south-1","ap-northeast-1","ap-northeast-2","ap-northeast-3","ap-southeast-1","ap-southeast-2","ca-central-1","eu-central-1","eu-west-1","eu-west-2","eu-west-3","eu-south-1","eu-north-1","me-south-1","sa-east-1"],"description":"AWS region hosting the DynamoDB table. Must match the region where the table is\ndeployed so the integration can reach it.","default":"us-east-1"},"method":{"type":"string","enum":["query"],"description":"DynamoDB operation used to retrieve items. Only \"query\" is currently supported."},"tableName":{"type":"string","description":"Name of the DynamoDB table to query. Must exactly match an existing table (case-sensitive)."},"keyConditionExpression":{"type":"string","description":"Key condition determining which items the query retrieves. Must include a condition on\nthe partition key and may add sort-key conditions (equality, BETWEEN, begins_with).\nReference attribute names with \"#\" placeholders defined in expressionAttributeNames and\nvalues with \":\" placeholders defined in expressionAttributeValues."},"filterExpression":{"type":"string","description":"Filters query results on non-key attributes after the key condition is applied; omit to\nreturn all items matching the key condition. Uses the same \"#\" and \":\" placeholders\ndefined in expressionAttributeNames and expressionAttributeValues."},"projectionExpression":{"type":"array","items":{"type":"string"},"description":"Attributes to return from each item, reducing data transfer; omit to return all\nattributes. Each array element is one field, referenced via \"#\" placeholders defined in\nexpressionAttributeNames."},"expressionAttributeNames":{"type":"string","description":"JSON string mapping \"#\" placeholders to actual attribute names, e.g.\n{\"#pk\": \"customerId\"}. Placeholders defined here are used in keyConditionExpression,\nfilterExpression, and projectionExpression."},"expressionAttributeValues":{"type":"string","description":"JSON string mapping \":\" placeholders to comparison values, e.g. {\":status\": \"ACTIVE\"}.\nValues can be static or dynamic handlebars expressions such as {{lastExportDateTime}},\nand are referenced from keyConditionExpression and filterExpression."},"onceExportPartitionKey":{"type":"string","description":"Partition key attribute that uniquely identifies items when the export's type is \"once\".\nCeligo uses it to mark items as processed after a successful export, preventing the same\nitems from being exported again on subsequent runs."},"onceExportSortKey":{"type":"string","description":"Sort key attribute used together with onceExportPartitionKey to identify processed items\nwhen the table has a composite primary key. Omit for tables keyed by a partition key\nalone."},"pathToRecords":{"$ref":"#/components/schemas/PathToRecords"},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"}}},"PathToRecords":{"type":"string","maxLength":250,"description":"Path to the array of records inside each document or item fetched from the source; each array element is emitted as an individual record, which keeps records within the 5 MB page-size limit even when the source document exceeds it. Accepts dot notation (e.g. `data.orders`) or JSONPath for values starting with `$`, including unions (`$['orders','invoices'][*]`) and filter expressions (`$.items[?(@.qty>3)]`); a path that resolves to nothing emits zero records for that document or item. When omitted, each document or item is emitted as one record, exactly as before — HTTP and file-based exports configure this same capability through their existing `http.response.resourcePath` and `file.json`/`file.xml` `resourcePath` fields."},"FTP":{"type":"object","description":"Defines which files to retrieve from an FTP, FTPS, or SFTP server. Required when the\n_connectionId field references an FTP/SFTP connection; must not be included for other\nconnection types. directoryPath selects the folder, fileNameStartsWith/fileNameEndsWith\nfilter files by name, and backupDirectoryPath controls where files are moved after\nretrieval.","required":["directoryPath"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies partner-specific B2B settings for this export.\nWhen set, the export inherits the connector's pre-configured settings; omit to use only\nthe FTP connection details."},"directoryPath":{"type":"string","description":"Directory on the server to retrieve files from, either absolute or relative to the login\ndirectory; the FTP user must have read permission on it. Use forward slashes regardless\nof server OS — paths are case-sensitive on UNIX/Linux servers. Supports handlebars\ntemplates, e.g. archive/{{date 'YYYY-MM-DD'}}."},"fileNameStartsWith":{"type":"string","description":"Only retrieves files whose names start with this value (case-sensitive on most servers);\naccepts static text or handlebars templates. When combined with fileNameEndsWith, files\nmust match both."},"fileNameEndsWith":{"type":"string","description":"Only retrieves files whose names end with this value, commonly a file extension\n(case-sensitive on most servers). When combined with fileNameStartsWith, files must\nmatch both."},"backupDirectoryPath":{"type":"string","description":"Directory on the same server where files are moved after successful export, giving you\nan independent backup; if omitted, files are simply deleted from the source directory\nafter successful export unless `file.skipDelete` is true (Celigo also keeps its own copy\nof processed files for a set period). Supports static paths or handlebars templates."}}},"JDBC":{"type":"object","description":"Configuration object for JDBC (Java Database Connectivity) data integration exports.\n\nThis object is REQUIRED when the _connectionId field references a JDBC database connection\nand must not be included for other connection types. It defines how data is extracted\nfrom relational databases using SQL queries.\n\n**Jdbc export capabilities**\n- Execute custom SQL SELECT statements\n- Support for joins, aggregations, and functions\n- Flexible data retrieval from any accessible tables or views\n- Compatible with all major database systems\n**Critical:** WHAT BELONGS IN THIS OBJECT\n- `query` - **ALWAYS REQUIRED** - The SQL SELECT statement\n- `once` - **REQUIRED** when the export's Object Type is `\"once\"` (set _include_once: true)\n- **DO NOT** put `delta` inside this object - delta is handled via the query\n\n**Delta exports (type: \"delta\")**\nFor delta/incremental exports, do NOT populate a `delta` object inside `jdbc`.\nInstead, use `{{lastExportDateTime}}` or `{{currentExportDateTime}}` directly in the query:\n```json\n{\n  \"type\": \"delta\",\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE updatedAt > {{lastExportDateTime}}\"\n  }\n}\n```\n\n**Once exports (type: \"once\")**\nFor once exports (mark records as processed), populate `jdbc.once.query`:\n```json\n{\n  \"type\": \"once\",\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE exported = false\",\n    \"once\": {\n      \"query\": \"UPDATE customers SET exported = true WHERE id = {{record.id}}\"\n    }\n  }\n}\n```\n\n**Standard exports (type: null or not specified)**\nJust provide the query:\n```json\n{\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE status = 'ACTIVE'\"\n  }\n}\n```","required":["query"],"properties":{"formType":{"type":["string","null"],"enum":["sql","simple",null],"description":"Selects how the export's query is authored. Use `sql` for a hand-written SQL statement in\n`query`; use `simple` to build the query from the `simple` object's table/columns/filter."},"query":{"type":"string","description":"SQL SELECT statement executed to retrieve data, from simple table selections to joins and\naggregations. For delta exports, include {{lastExportDateTime}}/{{currentExportDateTime}}\nin the WHERE clause rather than configuring a separate delta object."},"once":{"type":"object","description":"**CRITICAL: REQUIRED when the export's Object Type is \"once\".**\n\nIf Object Type is \"once\", you MUST set _include_once to true (or include this object).\n\nThis object has ONLY ONE property: \"query\" (a SQL UPDATE string).\nDO NOT create any other properties like \"update\", \"table\", \"set\", \"where\", etc.\n\nCORRECT format:\n```json\n{\"query\": \"UPDATE customers SET exported=true WHERE id={{record.id}}\"}\n```\n\nWRONG format (DO NOT DO THIS):\n```json\n{\"update\": {\"table\": \"customers\", \"set\": {...}}}\n```\n","properties":{"query":{"type":"string","description":"**REQUIRED** - A SQL UPDATE statement string to mark records as processed.\n\nThis is a plain SQL UPDATE query string, NOT a structured object.\n\nThe query runs AFTER each record is successfully exported, setting a flag\nto indicate the record has been processed.\n\nFormat: \"UPDATE <table> SET <column>=<value> WHERE <id_column>={{record.<id_field>}}\"\n\nExample: \"UPDATE customers SET exported=true WHERE id={{record.id}}\"\n\nThe {{record.id}} placeholder is replaced with the actual record ID from each exported row.\n"}}},"simple":{"type":"object","description":"Visual query-builder configuration used when `formType` is `simple`; the platform builds\nthe SELECT from these instead of raw SQL. Only relevant when `formType` is `simple`.","properties":{"table":{"type":"string","description":"Table or view to select rows from."},"columns":{"type":"array","items":{"type":"string"},"description":"Columns to return; an empty list selects all columns."},"filter":{"description":"Filter applied as the WHERE clause for the generated query, stored as the\nplatform's structured filter-rules object.","allOf":[{"$ref":"#/components/schemas/Filter"}]}}}}},"MongoDB":{"type":"object","description":"Defines how documents are retrieved from MongoDB collections. Required, with `method` and\n`collection`, when the _connectionId field references a MongoDB connection and the export\nfetches documents; must not be included for other connection types. A change-stream\nlistener (`type: stream`) is configured through `cdc` instead and may omit this object, or\nset only `collection` to scope the listener. Supports find queries with optional filter\ncriteria and field projections.","properties":{"method":{"type":"string","enum":["find"],"description":"MongoDB operation used to retrieve documents. Only \"find\" is currently supported,\nequivalent to db.collection.find(filter, projection)."},"collection":{"type":"string","description":"Name of the MongoDB collection to query. Case-sensitive and must reference an existing\ncollection in the connected database. Required on find exports; on a change-stream\nlistener (`type: stream`) it is optional and may list several collections separated by\ncommas, matching the listener form's \"Collections\" field."},"filter":{"type":"string","description":"MongoDB query document, as a JSON string, that selects which documents to export; omit\nto return every document in the collection. Supports standard MongoDB query operators\nand handlebars variables for dynamic values — for example\n{\"lastModified\": {\"$gt\": \"{{lastRun}}\"}} for incremental processing."},"projection":{"type":"string","description":"MongoDB projection document, as a JSON string, that limits which fields are returned;\nomit to return all fields. Use 1 to include fields or 0 to exclude them — the two modes\ncannot be mixed except for _id, which is included by default unless explicitly excluded.\nProjection affects only the shape of returned documents, not which documents match."},"pipeline":{"type":"string","description":"MongoDB aggregation pipeline, as a JSON-array string of stages, used instead of a plain\nfind when documents need to be transformed, joined, or grouped server-side (e.g. `$match`,\n`$lookup`, `$group`). When set, it takes the place of `filter`/`projection`. Supports\nhandlebars variables for incremental processing."},"readPreference":{"type":"string","enum":["doNotOverride","primary","primaryPreferred","secondary","secondaryPreferred","nearest"],"default":"doNotOverride","description":"Overrides which replica-set member this export reads from, trading consistency for load\ndistribution. Leave at `doNotOverride` to inherit the connection's setting."},"pathToRecords":{"$ref":"#/components/schemas/PathToRecords"},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"}}},"NetSuite":{"type":"object","description":"NetSuite-specific export configuration. Required when `_connectionId` references a NetSuite\nconnection; omit for all other connection types. Supports saved-search, RESTlet, and\ndistributed (SuiteApp) exports, plus two file cabinet modes: blob exports transfer files\nas-is (set the export's top-level `type` to `blob` and configure `netsuite.blob`), while\nfile exports parse file contents into records (leave the export's top-level `type` unset\nand configure `netsuite.file`).","properties":{"type":{"type":"string","enum":["search","basicSearch","metadata","selectoption","restlet","getList","getServerTime","distributed","file"],"description":"Controls how data is retrieved from NetSuite and which sibling object must be configured:\n`search` pairs with `netsuite.searches`, `restlet` with `netsuite.restlet`, `distributed`\nwith `netsuite.distributed`, and `file` with `netsuite.file.folderInternalId`. For blob\nexports (raw file transfer without parsing), leave this field unset, set the export's\ntop-level `type` to `blob`, and configure `netsuite.internalId` instead. For lookups\n(`isLookup: true`), prefer `restlet`, which supports `suiteapp2.0` saved searches with\ndynamic inputs; `search` is limited for dynamic lookups."},"searches":{"type":"array","description":"Saved search configurations that query NetSuite for records. Each entry references a\nsaved search by internal ID, names the record type being searched, and can add filter\ncriteria.","items":{"type":"object","properties":{"savedSearchId":{"type":"string","description":"Internal ID of the NetSuite saved search to run."},"recordType":{"type":"string","description":"NetSuite record type being searched, as its exact lowercase script ID\n(e.g. \"customer\", \"salesorder\") — not the display name."},"criteria":{"type":"array","description":"Additional filter criteria applied on top of the saved search.","items":{"type":"object","properties":{"field":{"type":"string","description":"Script-id of the search column to filter on.  REQUIRED.\nMust come from the record type's SEARCH COLUMNS list\n(NOT the body-fields list).  Becomes the `name`\nargument of NetSuite's `search.createFilter()`."},"operator":{"type":"string","description":"Comparison operator.  REQUIRED.  Supported values\ndepend on the column's data type (e.g. `is`,\n`isnot`, `after`, `before`, `anyof`,\n`noneof`, `contains`, `startswith`,\n`greaterthan`, `lessthan`).  Must be a string\nNetSuite recognises -- mismatched operators cause\nruntime rejection."},"join":{"type":"string","description":"Optional join name when filtering through a related\nrecord (e.g. `customer` from a sales order).  Use\nonly when the column lives on a related record and\nNetSuite supports the join from the base record type."},"searchValue":{"description":"Value (or array of values for `anyof`-style\noperators) to compare against the column.  Required\nfor most operators; omit for the few unary operators\n(e.g. `isempty`, `isnotempty`)."}},"required":["field","operator"]}}}}},"metadata":{"type":"object","description":"Supplemental metadata associated with the NetSuite export."},"selectoption":{"type":"object","description":"Configuration for `selectoption` exports, which retrieve the available select options\nfor a NetSuite field."},"customFieldMetadata":{"type":"object","description":"Metadata describing the custom fields defined in the NetSuite account."},"skipGrouping":{"type":"boolean","description":"When true, each saved-search result row becomes its own record. When false or omitted,\nconsecutive result rows that share the record's internal id are grouped into one record\nshaped as an array of rows — the *Group rows* option in the UI."},"statsOnly":{"type":"boolean","description":"When true, returns only summary statistics about matching records instead of the\ndetailed records themselves."},"internalId":{"type":"string","description":"Internal ID of the file in the NetSuite file cabinet to export. Required for blob\nexports (the export's top-level `type` is `blob`); the file is transferred as-is\nwithout parsing. For parsed file exports, use `netsuite.file.folderInternalId` instead."},"blob":{"type":"object","properties":{"purgeFileAfterExport":{"type":"boolean","description":"When true, permanently deletes the file from the NetSuite file cabinet after a\nsuccessful export. When false or omitted, the file is left in place."}},"description":"Configuration for blob exports, which transfer files from the NetSuite file cabinet\nas-is without parsing them into records. To export a blob, set the export's top-level\n`type` to `blob`, set `netsuite.internalId` to the file's internal ID, and leave\n`netsuite.type` unset. Use `netsuite.file` instead when file contents should be parsed\ninto records."},"restlet":{"type":"object","properties":{"recordType":{"type":"string","description":"NetSuite record type the RESTlet operates on, as its script ID. Custom record\ntypes use the \"customrecord_\" prefix."},"batchSize":{"type":"number","description":"Number of records the RESTlet returns per request (default 1000). This, not the\nexport's `pageSize`, sizes each NetSuite call; the export keeps requesting until the\nsearch is exhausted. Larger batches reduce round-trips but raise NetSuite governance\nand memory use per call; tune to the record's size. The RESTlet caps a call at 1000,\nso a larger value acts as 1000. In a test run or preview the request is capped at the\nexport's `pageSize` (default 20) instead."},"searchId":{"type":["string","null"],"description":"Internal ID of the NetSuite saved search the RESTlet executes. Results follow the\nsaved search's current definition, so editing the search in NetSuite changes the\nexport output without changing this ID."},"useSS2Restlets":{"type":"boolean","description":"When true, calls SuiteScript 2.0 RESTlets. When false or omitted, legacy\nSuiteScript 1.0 RESTlets are used.\nSet at step creation together with restletVersion — the same\ncreation-time-only constraint applies (see restletVersion)."},"restletVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"RESTlet version the export invokes. Defaults to `suiteapp2.0` when\n`useSS2Restlets` is true, `suitebundle` otherwise.\nThe version is fixed when the step is created — the Advanced selector is\ndisabled on existing steps, and migrating an existing export to a\ndifferent version means recreating or cloning it."},"criteria":{"type":"array","description":"Filter conditions added on top of the saved search (the UI's \"Additional search\ncriteria\"). Each condition pairs a search column with an operator and one or two\ncomparison values, and the conditions are combined with AND — every one must match.","items":{"type":"object","properties":{"field":{"type":"string","description":"Script-id of the search column to filter on. This is the primary\nfield the criterion is evaluated against (e.g. \"trandate\", \"status\")."},"join":{"type":"string","description":"Join relationship that applies this filter to a related record's fields instead\nof the base record. Must match a join name NetSuite defines for the record type."},"operator":{"type":"string","enum":["after","allof","any","anyof","before","between","contains","doesnotcontain","doesnotstartwith","equalto","greaterthan","greaterthanorequalto","haskeywords","is","isempty","isnot","isnotempty","lessthan","lessthanorequalto","noneof","notafter","notallof","notbefore","notbetween","notequalto","notgreaterthan","notlessthan","notlessthanorequalto","noton","notonorafter","notonorbefore","notwithin","on","onorafter","onorbefore","startswith","within"],"description":"Comparison operator applied between the column and the search value, using\nNetSuite's lowercase search operator names. Which operators a column accepts\ndepends on its data type."},"searchValue":{"description":"Value the column is compared against — a string, a number, or an array of\nvalues for `anyof`-style operators. On lookup steps it may be a Handlebars\nexpression rendered from the incoming record (for example `{{poNumber}}`)."},"searchValue2":{"description":"Second comparison value for the two-value operators `between` and `notbetween`\n(the upper bound). Omit for every other operator."},"formula":{"type":"string","description":"NetSuite formula expression used as the filter condition, for criteria that\nstandard field-operator-value comparisons can't express."}}}},"columns":{"type":"array","description":"Search columns the RESTlet returns for each matching record. With a saved search,\nthe RESTlet returns the search's own result columns followed by any listed here: a\nlist adds columns and never removes one of the search's, so remove a column in the\nsaved search itself. An ad-hoc search (no `searchId`) must list its columns here.","items":{"type":"object","properties":{"_id":{"type":"string","readOnly":true,"description":"Subdocument id the server assigns to the column entry on save. Not a NetSuite\nfield id — never set it when authoring."},"name":{"type":"string","description":"Column field name as referenced in the search."},"join":{"type":"string","description":"Join relationship that sources this column from a related record instead of\nthe base record. Supports dot notation for nested joins (e.g. \"employee.manager\")."},"summary":{"type":"string","enum":["group","sum","count","avg","min","max"],"description":"Summary type applied to this column, turning the search into a summary search."},"formula":{"type":"string","description":"NetSuite formula expression that computes this column's value at runtime, such\nas a CASE statement or date formatting, instead of reading a stored field."},"label":{"type":"string","description":"Display name for the column; it becomes the property name of the column's value\nin each exported record."},"sort":{"type":"boolean","description":"When true, query results are sorted by this column's values."}}}},"markExportedBatchSize":{"type":"integer","description":"Number of records updated per RESTlet call when marking records as exported in\nNetSuite (live: an integer count, e.g. 100).","maximum":100,"minimum":1},"hooks":{"type":["object","null"],"properties":{"batchSize":{"type":"number","description":"Legacy location of the RESTlet page size; `restlet.batchSize` is the current\nfield and takes precedence when both are set."},"preSend":{"type":"object","properties":{"fileInternalId":{"type":"string","description":"Internal ID of the JavaScript file in the NetSuite File Cabinet that defines\nthe hook function. Must be readable by the role behind the export's connection."},"function":{"type":"string","description":"Name of the global function in that file that NetSuite invokes as the pre\nsend hook."},"configuration":{"type":"object","description":"Free-form JSON stored with the hook reference for the script's own use. Custom\nsettings in scope for the export are passed to the function separately as\n`options.settings`."}},"description":"SuiteScript hook that runs inside NetSuite after a page of records is collected\nand before it is sent to the Celigo platform."}},"description":"SuiteScript hooks for the export. Only `preSend` exists on exports; imports carry\nthe pre map, post map, and post submit hooks."},"cLocked":{"type":"object","description":"Lock state that prevents modification of the configuration when set."}},"description":"Configuration for `restlet` exports. Identifies the RESTlet script that retrieves data\nfrom NetSuite, along with the saved search, criteria, and columns it executes with."},"distributed":{"type":"object","properties":{"recordType":{"type":"string","description":"NetSuite record type the listener is deployed on, as its lowercase script ID\n(`customer`, `salesorder`, `customrecord_<scriptid>`) — never the display name or a\ncustom record's numeric internal ID. Types the platform's record-type catalog marks\n`doesNotSupportRTExport` cannot host a listener and are not offered in the UI."},"executionContext":{"type":"array","description":"NetSuite execution contexts that trigger this distributed export. A record change\nfires the export only when it occurs in one of the listed contexts.","default":["userinterface","webstore"],"items":{"type":"string","enum":["userinterface","webservices","csvimport","offlineclient","portlet","scheduled","suitelet","custommassupdate","workflow","webstore","userevent","mapreduce","restlet","webapplication","restwebservices"]}},"disabled":{"type":"boolean","description":"When true, the listener's NetSuite deployment is inactive and record saves no longer\ntrigger the export. Maintained by the platform as the flow is disabled and re-enabled;\nre-enabling the flow re-registers the deployment. A listener is created disabled, and\na flow created already enabled leaves it that way until a flow update sets\n`disabled: false`, which activates it within seconds."},"executionType":{"type":"array","description":"Record operations that trigger this distributed export. A record event fires the\nexport only when its operation matches one of the listed types.","default":["create","edit","xedit"],"items":{"type":"string","enum":["create","edit","delete","xedit","copy","view","cancel","approve","reject","pack","ship","markcomplete","reassign","editforecast","dropship","specialorder","orderitems","paybills","print","email"]}},"qualifier":{"type":["array","string","null"],"description":"Field-specific qualification criteria the SuiteApp evaluates against the record before\nexporting it; saves that do not match are discarded. Stored as a filter-expression array\nof `[field, operator, value]` rules joined by lowercase `\"and\"` / `\"or\"`, as a legacy\nexpression string, or null when no criteria are set."},"skipExportFieldId":{"type":"string","description":"Script ID of a checkbox body field on the record type. A save with that box checked is\nskipped by the listener, which then clears the box itself — used to keep a mass update\nfrom fanning out and to stop a write-back import from re-triggering the listener."},"hooks":{"type":"object","description":"SuiteScript hook that runs inside NetSuite before the record leaves it.","properties":{"preSend":{"type":"object","description":"Pre-send hook: a SuiteScript function in the NetSuite file cabinet invoked before\nrecords leave NetSuite, with the full SuiteScript API available — for example to run\ndynamic searches against the record being exported.","properties":{"fileInternalId":{"type":"string","description":"Internal ID of the file cabinet file that holds the hook's SuiteScript."},"function":{"type":"string","description":"Name of the function in that file to invoke for each export."},"configuration":{"type":"object","description":"Settings passed to the hook function."}}}}},"sublists":{"type":["array","null"],"description":"IDs of the NetSuite sublists to include with each exported record (for example `item`\nfor transaction line items). Records are exported with body fields only when no\nsublists are selected. Legacy documents may store null (equivalent to no sublists).","items":{"type":"string"}},"referencedFields":{"type":"object","description":"Field references recorded on the listener by the platform. Not exposed in the listener\nform; leave as stored."},"relatedLists":{"type":"object","description":"Related record lists recorded on the listener by the platform. Not exposed in the\nlistener form; leave as stored."},"forceReload":{"type":"boolean","description":"When true, the listener reloads the record from the database after the save before\nexporting it, so fields that are populated only once the save completes — such as\ntransaction line IDs on new lines — are included. The reload slows every save of that\nrecord type."},"ioEnvironment":{"type":"string","description":"integrator.io environment the listener's NetSuite deployment reports to. Written by the\nplatform when the listener registers with NetSuite; live-observed values include\n`production` and `staging`."},"ioDomain":{"type":"string","description":"integrator.io domain the listener's NetSuite deployment calls back to — the account's\nown regional domain, stamped by the platform on every save and echoed on create and\nupdate responses; not returned by GET."},"lastSyncedDate":{"type":"string","format":"date-time","description":"When the listener's deployment last synchronized with NetSuite. Written by the platform;\nnot set until the first successful synchronization."},"settings":{"type":"object","description":"Additional key-value settings for the distributed export. Not exposed in the listener\nform."},"useSS2Framework":{"type":"boolean","description":"Derived by the platform from `frameworkVersion` — true when it is `suiteapp2.0`, absent\notherwise — and echoed on create and update responses; not returned by GET. A value\nsent on create or update is replaced by the derived one."},"frameworkVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"SuiteApp framework version used by the distributed export.\nFixed when the step is created — the Advanced selector is disabled on\nexisting steps, and migrating an existing listener to a different\nversion means recreating or cloning it."}},"description":"Configuration for `distributed` exports, which use the NetSuite SuiteApp to fire\nreal-time, event-driven exports when records change. Define the record type to listen\nto and the execution contexts and operations that trigger it."},"getList":{"type":"array","description":"Configuration for `getList` exports — a list of record references, each retrieving one\nNetSuite record by its identifier. The export returns the fetched records in list order.","items":{"type":"object","properties":{"type":{"type":"string","description":"Standard NetSuite record type to retrieve (e.g. \"customer\", \"salesOrder\").\nUsed when `typeId` is absent. Case-sensitive."},"typeId":{"type":"string","description":"Script ID of a custom record type (\"customrecord_...\") or custom transaction\ntype (\"customtransaction_...\"). Takes precedence over `type` when present.\nCase-sensitive."},"internalId":{"type":"string","description":"Internal ID of the NetSuite record to retrieve. Assigned by NetSuite at record\ncreation and unique within the record type. Takes precedence over `externalId`."},"externalId":{"type":"string","description":"Identifier assigned by an external system, used to reference and synchronize the\nrecord across systems. Distinct from the NetSuite-assigned internal ID."}}}},"searchPreferences":{"type":"object","properties":{"bodyFieldsOnly":{"type":"boolean","description":"When true, search results include only the record's body fields and exclude fields\nfrom joined or related records. Reduces payload size when related data isn't needed."},"pageSize":{"type":"number","description":"Number of search results NetSuite returns per page."},"returnSearchColumns":{"type":"boolean","description":"When true, search results include the column data defined in the search. When\nfalse, column data is omitted and results contain only minimal record information."}},"description":"Preferences that control how NetSuite executes searches and shapes the results it returns."},"file":{"type":"object","description":"Configuration for file exports, which retrieve files from the NetSuite file cabinet\nand parse their contents (CSV, XML, JSON) into records. Leave the export's top-level\n`type` unset when using this object; for raw transfers without parsing, use\n`netsuite.blob` with the export's `type` set to `blob` instead.","properties":{"folderInternalId":{"type":"string","description":"Internal ID of the NetSuite file cabinet folder to export files from. Accepts a\nhandlebars expression (e.g. `{{record.folderId}}`) when the folder must be\nselected dynamically from record data. The ID is stable across folder renames and\nmoves, but may differ between non-production and production accounts."},"backupFolderInternalId":{"type":"string","description":"Internal ID of the NetSuite file cabinet folder where backup files are stored.\nIDs are account-specific and do not transfer between non-production and production\nenvironments."},"fileNameStartsWith":{"type":"string","description":"Optional prefix filter. Only files whose names start with this string are\nexported from the configured folder."},"fileNameEndsWith":{"type":"string","description":"Optional suffix filter. Only files whose names end with this string (e.g. a\n\".csv\" extension) are exported from the configured folder."}}}},"required":[]},"RDBMS":{"type":"object","description":"Configuration object for Relational Database Management System (RDBMS) data integration exports.\n\nThis object defines how data is read from a relational database and must not be included\nfor other connection types. For query-type exports (standard, delta, once) it is REQUIRED\nwhen the _connectionId field references an RDBMS database connection and holds the SQL\nquery. For real-time stream listeners (`type: \"stream\"`) it holds the CDC watch list\n(`tables`) instead — and a PostgreSQL listener scoped by a publication\n(`cdc.publicationName`) may omit this object entirely.\n\n**Rdbms export capabilities**\n- Execute custom SQL SELECT statements\n- Support for joins, aggregations, and functions\n- Flexible data retrieval from any accessible tables or views\n- Compatible with all major database systems\n\n**Critical:** WHAT BELONGS IN THIS OBJECT\n- `query` - The SQL SELECT statement - required for every query-type export (standard, delta, once)\n- `once` - **REQUIRED** when the export's Object Type is `\"once\"` (set _include_once: true)\n- `tables` - the CDC listener watch list - real-time stream listeners (`type: \"stream\"`) only\n- **DO NOT** put `delta` inside this object - delta is handled via the query\n- **DO NOT** author `query` on a stream listener - real-time CDC exports have no SQL query\n\n**Delta exports (type: \"delta\")**\nFor delta/incremental exports, do NOT populate a `delta` object inside `rdbms`.\nInstead, use `{{lastExportDateTime}}` or `{{currentExportDateTime}}` directly in the query, and\nset the export's top-level `delta.startDate`: without it the first run's window is empty.\n```json\n{\n  \"type\": \"delta\",\n  \"delta\": { \"startDate\": \"2026-01-01T00:00:00.000Z\" },\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE updatedAt > {{lastExportDateTime}}\"\n  }\n}\n```\n\n**Once exports (type: \"once\")**\nFor once exports (mark records as processed), populate `rdbms.once.query`:\n```json\n{\n  \"type\": \"once\",\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE exported = false\",\n    \"once\": {\n      \"query\": \"UPDATE customers SET exported = true WHERE id = {{record.id}}\"\n    }\n  }\n}\n```\n\n**Standard exports (type: null or not specified)**\nJust provide the query:\n```json\n{\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE status = 'ACTIVE'\"\n  }\n}\n```\n\n**Real-time CDC listener exports (type: \"stream\")**\nSQL Server and PostgreSQL connections support change-data-capture listeners that stream row\nchanges continuously (the `cdc` object holds the listener settings). A stream export has NO\n`query` — it watches `tables`:\n```json\n{\n  \"type\": \"stream\",\n  \"rdbms\": { \"tables\": \"public.orders,public.customers\" },\n  \"cdc\": { \"properties\": [{ \"name\": \"snapshot.mode\", \"value\": \"no_data\" }] }\n}\n```\nA PostgreSQL listener can instead scope tables through a publication\n(`cdc.publicationName`), in which case `tables` stays unset and the `rdbms` object may be\nomitted entirely.","properties":{"tables":{"type":"string","description":"Comma-separated list of tables the CDC listener watches for changes. Used only by real-time\nlistener exports (`type` is `stream`); standard SQL exports use `query` instead. SQL Server\nlisteners use fully-qualified three-part names (`database.schema.table`); PostgreSQL\nlisteners use two-part names (`schema.table`)."},"query":{"type":"string","description":"SQL SELECT statement executed to retrieve data, from simple table selections to joins,\naggregations, and parameterized queries with Handlebars expressions. Query-type exports\nonly — real-time stream listeners (`type` is `stream`) have no SQL query. For delta exports,\nreference {{lastExportDateTime}}/{{currentExportDateTime}} directly in the query rather\nthan configuring a separate delta object.\n\nDouble braces render a string as a quoted SQL literal with its apostrophes escaped, and\ntriple braces insert a value raw. Observed in Snowflake queries:\n\n- String helper output (`dateFormat`, `jsonSerialize`) and both delta variables are\n  quoted the same way; a number or boolean renders bare, whether it is a JSON value or a\n  numeric helper's result (`{{add 2 3}}` renders `5`).\n- In a run, {{lastExportDateTime}} and {{currentExportDateTime}} arrive as quoted ISO 8601\n  UTC literals with milliseconds (`'2026-01-15T00:00:00.000Z'`), or in `delta.dateFormat`\n  when it is set: add no quotes of your own, and don't parse them with a format mask\n  written for another format.\n- A preview of an unsaved export document renders both as empty, unquoted text, so a\n  delta query fails to compile there unless the document carries both in `postData`.\n- The quoting leaves backslashes, which Snowflake reads as escape sequences, so a\n  double-brace value holding a backslash arrives changed. Pass such a value in a\n  dollar-quoted constant with triple braces (`$${{{record.field}}}$$`), which Snowflake\n  reads as is, but only when it can't contain `$$`: a `$$` ends the constant, and the rest\n  is read as SQL."},"once":{"type":"object","description":"**CRITICAL: REQUIRED when the export's Object Type is \"once\".**\n\nIf Object Type is \"once\", you MUST set _include_once to true (or include this object).\n\nThis object has ONLY ONE property: \"query\" (a SQL UPDATE string).\nDO NOT create any other properties like \"update\", \"table\", \"set\", \"where\", etc.\n\nCORRECT format:\n```json\n{\"query\": \"UPDATE customers SET exported=true WHERE id={{record.id}}\"}\n```\n\nWRONG format (DO NOT DO THIS):\n```json\n{\"update\": {\"table\": \"customers\", \"set\": {...}}}\n```\n","properties":{"query":{"type":"string","description":"**REQUIRED** - A SQL UPDATE statement string to mark records as processed.\n\nThis is a plain SQL UPDATE query string, NOT a structured object.\n\nThe query runs AFTER each record is successfully exported, setting a flag\nto indicate the record has been processed.\n\nFormat: \"UPDATE <table> SET <column>=<value> WHERE <id_column>={{record.<id_field>}}\"\n\nExample: \"UPDATE customers SET exported=true WHERE id={{record.id}}\"\n\nThe {{record.id}} placeholder is replaced with the actual record ID from each exported row.\n"}}}}},"S3":{"type":"object","description":"Defines which files to retrieve from an Amazon S3 bucket. Required when the _connectionId\nfield references an AWS S3 connection; must not be included for other connection types.\nregion and bucket locate the source, keyStartsWith/keyEndsWith filter objects by key, and\nbackupBucket with keyPrefix controls where files are moved after retrieval.","required":["region","bucket"],"properties":{"region":{"type":"string","default":"us-east-1","description":"AWS region where the bucket is located. Case-insensitive; the value is normalized to\nlowercase."},"bucket":{"type":"string","description":"S3 bucket to retrieve files from. The connection's AWS credentials must have\ns3:ListBucket and s3:GetObject permissions on it."},"keyStartsWith":{"type":"string","description":"Only retrieves objects whose keys start with this value (case-sensitive), effectively\nselecting a folder in S3's flat key structure. When combined with keyEndsWith, objects\nmust match both."},"keyEndsWith":{"type":"string","description":"Only retrieves objects whose keys end with this value (case-sensitive), commonly a file\nextension. When combined with keyStartsWith, objects must match both."},"backupBucket":{"type":"string","description":"Bucket in the same region where files are moved after successful export, giving you an\nindependent backup; if omitted, files are simply deleted from the source bucket after\nsuccessful export unless `file.skipDelete` is true (Celigo also keeps its own copy of\nprocessed files for a set period). The connection's AWS credentials must have\ns3:PutObject permission on this bucket."},"keyPrefix":{"type":"string","description":"Prefix prepended to each file's name when it is moved to the backup bucket; accepts\nstatic text or handlebars templates. The original directory structure is not preserved —\nonly the filename is appended to this prefix. Ignored unless backupBucket is set."}}},"Wrapper":{"type":"object","description":"Configuration for Wrapper exports, which delegate data retrieval to custom connector code\n(typically a stack-hosted function) rather than a built-in adaptor. Required when the\n_connectionId field references a wrapper connection.","required":["function"],"properties":{"function":{"type":"string","description":"Name of the function the wrapper invokes to retrieve records. Must match a callable\nfunction in the wrapper's execution context; names are case-sensitive."},"configuration":{"oneOf":[{"type":"object","additionalProperties":true},{"type":"array","maxItems":0}],"description":"Free-form settings passed to the wrapper function at runtime (connector-specific keys such\nas method, apiVersion, headers, relativePath, or body). Structure is defined by the wrapper\ncode, not by this schema. May be stored as an empty array when no settings exist."}}},"Parsers":{"type":"array","description":"Configuration for parsing XML payloads (i.e. files, HTTP responses, etc.). Use this field when you need to process XML data\nand transform it into a JSON records.\n\n**Implementation notes**\n\n- This is where you configure how to parse XML data in your resource\n- Although defined as an array, you typically only need a single parser configuration\n- Currently only XML parsing is supported\n- Only configure this field when working with XML data that needs structured parsing\n","items":{"type":"object","properties":{"version":{"type":"string","description":"Version identifier for the parser configuration format. Currently only version \"1\" is supported.\n\nAlways set this field to \"1\" as it's the only supported version at this time.\n","enum":["1"]},"type":{"type":"string","description":"Defines the type of parser to use. Currently only \"xml\" is supported.\n\nWhile the system is designed to potentially support multiple parser types in the future,\nat this time only XML parsing is implemented, so this field must be set to \"xml\".\n","enum":["xml"]},"name":{"type":"string","description":"Optional identifier for the parser configuration. This field is primarily for documentation\npurposes and is not functionally used by the system.\n\nThis field can be omitted in most cases as it's not required for parser functionality.\n"},"rules":{"type":"object","description":"Configuration rules that determine how XML data is parsed and converted to JSON.\nThese settings control the structure and format of the resulting JSON records.\n\n**Parsing options**\n\nThere are two main parsing strategies available:\n- **Automatic parsing**: Simple but produces more complex output\n- **Custom parsing**: More control over the resulting JSON structure\n","properties":{"V0_json":{"type":"boolean","description":"Controls the XML parsing strategy.\n\n- When set to **true** (Automatic): XML data is automatically converted to JSON without\n  additional configuration. This is simpler to set up but typically produces more complex\n  and deeply nested JSON that may be harder to work with.\n\n- When set to **false** (Custom): Gives you more control over how the XML is converted to JSON.\n  This requires additional configuration (like listNodes) but produces cleaner, more\n  predictable JSON output.\n\nMost implementations use the Custom approach (false) for better control over the output format.\n"},"listNodes":{"type":"array","description":"Paths of elements that always parse as arrays. An element that can repeat parses as an\nobject when it occurs once and as an array when it occurs more than once, so list every\nrepeating element here to give each record the same shape regardless of item count.\nEach entry is the element's full path from the record element selected by the resource\npath (from the document root when there is no resource path), with an optional leading\nslash. Only relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"includeNodes":{"type":"array","description":"Limits which XML nodes are included in the output JSON.\n\nFor large XML documents, you can use this field to extract only the nodes you need,\nreducing the size and complexity of the resulting JSON. Only nodes specified here\n(and their children) will be included in the output.\n\nEach entry should be a simplified XPath expression pointing to nodes to include.\n\nOnly relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"excludeNodes":{"type":"array","description":"Specifies which XML nodes should be excluded from the output JSON.\n\nSometimes it's easier to specify which nodes to exclude rather than which to include.\nUse this field to identify nodes that should be omitted from the output JSON.\n\nEach entry should be a simplified XPath expression pointing to nodes to exclude.\n\nOnly relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"stripNewLineChars":{"type":"boolean","description":"Controls whether newline characters are removed from text values.\n\nWhen set to true, all newline characters (\\n, \\r, etc.) will be removed from\ntext content in the XML before conversion to JSON.\n","default":false},"trimSpaces":{"type":"boolean","description":"Controls whether leading and trailing whitespace is trimmed from text values.\n\nWhen set to true, all values will have leading and trailing whitespace removed\nbefore conversion to JSON.\n","default":false},"attributePrefix":{"type":"string","description":"Specifies a character sequence to prepend to XML attribute names when converted to JSON properties.\n\nIn XML, both elements and attributes can exist at the same level, but in JSON this distinction is lost.\nTo maintain the distinction between element data and attribute data in the resulting JSON, this prefix\nis added to attribute names during conversion.\n\nFor example, with attributePrefix set to \"Att-\" and an XML element like:\n```xml\n<product id=\"123\">Laptop</product>\n```\n\nThe resulting JSON would be:\n```json\n{\n  \"product\": \"Laptop\",\n  \"Att-id\": \"123\"\n}\n```\n\nThis helps maintain the distinction between element content and attribute values in the\nconverted JSON, making it easier to reference specific data in downstream processing steps.\n"},"textNodeName":{"type":"string","description":"Specifies the property name to use for element text content when an element has both\ntext content and child elements or attributes.\n\nWhen an XML element contains both text content and other nested elements or attributes,\nthis field determines what property name will hold the text content in the resulting JSON.\n\nFor example, with textNodeName set to \"value\" and an XML element like:\n```xml\n<item id=\"123\">\n  Laptop\n  <category>Electronics</category>\n</item>\n```\n\nThe resulting JSON would be:\n```json\n{\n  \"item\": {\n    \"value\": \"Laptop\",\n    \"category\": \"Electronics\",\n    \"id\": \"123\"\n  }\n}\n```\n\nThis allows for unambiguous parsing of complex XML structures that mix text content with\nchild elements. Choose a name that's unlikely to conflict with actual element names in your XML.\n"}}}}}},"Mappings":{"type":"array","description":"Array of field mapping configurations for transforming data from one format into another.\n\n**Guidance**\n\nThis schema is designed around RECURSION as its core architectural principle. Understanding this recursive\nnature is essential for building effective mappings:\n\n1. The schema is self-referential by design - a mapping can contain nested mappings of the same structure\n2. Complex data structures (nested objects and arrays of objects, to any depth) are ALL\n   handled through this recursive pattern; an array of arrays is not (build one in a script hook)\n3. Each mapping handles one level of the data structure; deeper levels are handled by nested mappings\n\nWhen generating mappings programmatically:\n- For simple fields (string, number, boolean): Create single mapping objects\n- For objects: Create a parent mapping with nested 'mappings' array containing child field mappings\n- For arrays: Use 'buildArrayHelper' with extract paths defining array inputs and\n  recursive 'mappings' to define object structures\n\nThe system will process these nested structures recursively during runtime, ensuring proper construction\nof complex hierarchical data while maintaining excellent performance.\n\nOn save, a leaf row with neither a source (`extract`, `hardCodedValue`, `lookupName`) nor a\ndestination (`generate`) is dropped silently, at every nesting level, and so is an object or\nobject-array row left with no source, no destination and no remaining child `mappings` or\n`buildArrayHelper` entries; a row with only one of the two, and a source-less, destination-less\nwrapper that still has children, are kept.\n\nThe shape of the array is the mapper's **JSON structure**. Named top-level items produce an object\nrecord (`record { }`). Exactly one top-level item with `dataType: objectarray` and no `generate` is\nthe `rows [ ]` structure: the mapped record is an array — usually one element per element of the\nsource array that root's `buildArrayHelper` iterates — Handlebars templates such as `http.body`\nbind it as `rows` instead of `record`, and the mapping editor shows that row's destination disabled.\n","items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}}},"items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}},"Transform":{"type":"object","description":"Configuration for transforming data during processing operations. This object enables\nreshaping of records.\n\n**Transformation capabilities**\n\nCeligo's transformation engine offers powerful features for data manipulation:\n- Precise field mapping with JSONPath expressions\n- Support for any level of nested arrays\n- Formula-based field value generation\n- Dynamic references to flow and integration settings\n\n**Implementation approaches**\n\nThere are two distinct transformation mechanisms available:\n\n**Rule-Based Transformation (`type: \"expression\"`)**\n- **Best For**: Most transformation scenarios from simple to complex\n- **Capabilities**: Field mapping, formula calculations, lookups, nested data handling\n- **Advantages**: Visual configuration, no coding required, intuitive interface\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear mapping requirements or need to reshape data structure\n\n**Script-Based Transformation (`type: \"script\"`)**\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Capabilities**: Full programmatic control, custom processing, complex business rules\n- **Advantages**: Maximum flexibility, can implement any transformation logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Visual transformation tools aren't sufficient for your use case\n","properties":{"type":{"type":"string","description":"Determines which transformation mechanism to use. This choice affects which properties\nmust be configured and how transformation logic is implemented.\n\n**Available types**\n\n**Rule-Based Transformation (`\"expression\"`)**\n- **Required Config**: The `expression` object with mapping definitions\n- **Behavior**: Applies declarative rules to reshape data\n- **Best For**: Most transformation scenarios from simple to complex\n- **Advantages**: Visual configuration, no coding required\n\n**Script-Based Transformation (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to transform data\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard data transformations, use `\"expression\"`\n2. For complex logic or specialized processing, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based transformations. This object enables reshaping data\nwithout requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define transformation rules that\ncan map, modify, and generate data elements.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Field mappings define how input data is transformed to target fields\n- Formulas can be used to calculate or generate new values\n- Lookups can enrich data by fetching related information\n- Mode determines how records are processed (create new or modify existing)\n","properties":{"version":{"type":"string","description":"Version of the expression format. Determines which rules\nproperty contains the transformation logic.\n","enum":["1","2"]},"rules":{"type":"array","description":"Transformation rules for version 1 expressions. An array of\nrule groups; each group is an array of field-mapping objects.\nMost transforms have a single group. Present when `version`\nis `\"1\"`. The output record contains ONLY the generated\nfields — every unmapped field is dropped (v1 has no\nequivalent of Transform 2.0's `modify` mode), and the\nrecord's trace key does not survive the rebuild.\n","items":{"type":"array","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path to read from. Supports multiple\nsyntaxes: bare field names (`id`), dot notation\n(`fulfillment.shipment_id`), slash-prefixed paths\nfor XML (`/FeedProcessingStatus`), wildcards (`*.id`,\n`*.[Internal ID]`), and array indexing (`SDF[0]`).\n"},"generate":{"type":"string","description":"Target field name to write to. Typically a bare name\n(`id`) or dot path (`SDF.Filter.ID`).\n"},"key":{"type":"string","description":"Auto-generated identifier for this rule, used by the\nUI to track individual rules for editing and reordering.\n"}},"required":["extract","generate"]}}},"rulesTwoDotZero":{"type":"object","description":"Configuration for version 2 transformation rules. This object contains the core logic\nfor how data is mapped, enriched, and transformed.\n\n**Capabilities**\n\nTransformation 2.0 provides:\n- Precise field mapping with JSONPath expressions\n- Support for deeply nested data structures\n- Formula-based field generation\n- Dynamic lookups for data enrichment\n- Multiple operating modes to fit different scenarios\n","properties":{"mode":{"type":"string","description":"Transformation mode that determines how records are handled during processing.\n\n**Available modes**\n\n**Create Mode (`\"create\"`)**\n- **Behavior**: Builds entirely new output records from inputs\n- **Use When**: Output structure differs significantly from input\n- **Advantage**: Clean slate approach, no field inheritance\n\n**Modify Mode (`\"modify\"`)**\n- **Behavior**: Makes targeted edits to existing records\n- **Use When**: Output structure should remain similar to input\n- **Advantage**: Preserves unmapped fields from the original record\n","enum":["create","modify"]},"mappings":{"$ref":"#/components/schemas/Mappings"},"lookups":{"allOf":[{"description":"Shared lookup tables used across all mappings defined in the transformation rules.\n\n**Purpose**\n\nLookups provide centralized value translation that can be referenced from any mapping\nin your transformation configuration. They enable consistent translation of codes, IDs,\nand values between systems without duplicating translation logic.\n\n**Usage in transformations**\n\nLookups are particularly valuable in transformations for:\n\n- **Data Normalization**: Standardizing values from diverse source systems\n- **Code Translation**: Converting between different coding systems (e.g., status codes)\n- **Field Enrichment**: Adding descriptive values based on ID or code lookups\n- **Cross-Reference Resolution**: Mapping identifiers between integrated systems\n\n**Implementation**\n\nLookups are defined once in this array and referenced by name in mappings:\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"statusMapping\",\n    \"map\": {\n      \"A\": \"Active\",\n      \"I\": \"Inactive\",\n      \"P\": \"Pending\"\n    },\n    \"default\": \"Unknown Status\"\n  }\n]\n```\n\nThen referenced in mappings using the lookupName property:\n\n```json\n{\n  \"generate\": \"status\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.statusCode\",\n  \"lookupName\": \"statusMapping\"\n}\n```\n\nThe system automatically applies the lookup during transformation processing.\n\nFor complete details on lookup properties and behavior, see the Lookups schema.\n"},{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the JSON shape the transformTwoDotZero processor\nevaluates `mappings[].extract` JSONPath values against at\nflow runtime. Applies only to Transform 2.0 (v2,\n`rulesTwoDotZero`); v1 transforms (the `rules` array on\n`transform.expression.rules`) and script-mode transforms\nignore this field.\n"}}}}},"script":{"type":"object","description":"Configuration for programmable script-based transformations. This object enables complex, custom\ntransformation logic beyond what expression-based transformations can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to transform data according to\nspecialized business rules or complex algorithms.\n\n**Implementation approach**\n\nScript-based transformation works by:\n1. Executing the specified function from the referenced script\n2. Passing input data to the function\n3. Using the function's return value as the transformed output\n\n**Common use cases**\n\nScript transformation is ideal for:\n- Complex business logic that can't be expressed through mappings\n- Algorithmic transformations requiring computation\n- Dynamic transformations based on external factors\n- Legacy system data format compatibility\n- Multi-stage processing with intermediate steps\n\nOnly use script-based transformation when expression-based transformation is insufficient.\nScript transformation requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to a predefined script resource containing the transformation logic.\n\nThe referenced script should contain the function specified in the\n'function' property. It must be a script in this account: any other id fails the save\nwith 422 (\"Only a script owned by the user or a script shared by the Integration App can\nbe used at transform.script._scriptId.\").\n","format":"objectid"},"function":{"type":"string","description":"Name of the function within the script to execute for transformation. This function\nmust exist in the script referenced by _scriptId.\n"}}}}},"Lookups":{"type":"array","description":"Configuration for value-to-value transformations using lookup tables.\n\n**Purpose**\n\nLookups provide a way to translate values from one system to another. They transform\ninput values into output values using either static mapping tables or\ndynamic lookup caches.\n\n**Lookup mechanisms**\n\nThere are two distinct lookup mechanisms available:\n\n1. **Static Lookups**: Define a simple key-value map object and store it as part of your resource\n   - Best for: Small, fixed sets of values that rarely change\n   - Implementation: Configure the `map` object with input-to-output value mappings\n   - Example: Country codes, status values, simple translations\n\n2. **Dynamic Lookups**: Reference an existing 'Lookup Cache' resource in your Celigo account\n   - Best for: Large datasets, frequently changing values, or complex reference data\n   - Implementation: Configure `_lookupCacheId` to reference cached data maintained independently\n   - Example: Product catalogs, customer databases, pricing information\n\n**Property usage**\n\nThere are two mutually exclusive ways to configure lookups, depending on which mechanism you choose:\n\n1. **For Static Mappings**: Configure the `map` property with a direct key-value object\n   ```json\n   \"map\": {\"US\": \"United States\", \"CA\": \"Canada\"}\n   ```\n\n2. **For Dynamic Lookups**: Configure the following properties:\n   - `_lookupCacheId`: Reference to the lookup cache resource\n   - `extract`: JSON path to extract specific value from the returned lookup object\n\n**When to use**\n\nLookups are ideal for:\n\n1. **Value Translation**: Mapping codes or IDs to human-readable values\n\n2. **Data Enrichment**: Adding related information to records during processing\n\n3. **Normalization**: Ensuring consistent formatting of values across systems\n\n**Implementation details**\n\nLookups can be referenced in:\n\n1. **Field Mappings**: Direct use in field transformation configurations\n\n2. **Handlebars Templates**: Use within templates with the syntax:\n   ```\n   {{lookup 'lookupName' record.fieldName}}\n   ```\n\n**Example usage**\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"countryCodeToName\",\n    \"map\": {\n      \"US\": \"United States\",\n      \"CA\": \"Canada\",\n      \"UK\": \"United Kingdom\"\n    },\n    \"default\": \"Unknown Country\",\n    \"allowFailures\": true\n  },\n  {\n    \"name\": \"productDetails\",\n    \"_lookupCacheId\": \"60a2c4e6f321d800129a1a3c\",\n    \"extract\": \"$.details.price\",\n    \"allowFailures\": false\n  }\n]\n```\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for the lookup table within this configuration.\n\nThis name must be unique within the scope where the lookup is defined and is used to reference\nthe lookup in handlebars templates with the syntax {{lookup 'name' value}}.\n\nChoose descriptive names that indicate the transformation purpose, such as:\n- \"countryCodeToName\" for country code to full name conversion\n- \"statusMapping\" for status code translations\n- \"departmentCodes\" for department code to name mapping\n"},"map":{"type":["object","null"],"description":"The lookup mapping table as key-value pairs. The platform stores `null`\nhere on dynamic lookups, which resolve values at runtime instead of\nfrom a static table.\n\nThis object contains the input values as keys and their corresponding\noutput values. When a input value matches a key in this object,\nit will be replaced with the corresponding value.\n\nThe map should be kept to a reasonable size (typically under 100 entries)\nfor optimal performance. For larger mapping requirements, consider using\ndynamic lookups instead.\n\nMaps can include:\n- Simple code to name conversions: {\"US\": \"United States\"}\n- Status transformations: {\"A\": \"Active\", \"I\": \"Inactive\"}\n- ID to name mappings: {\"100\": \"Marketing\", \"200\": \"Sales\"}\n\nValues can be strings, numbers, or booleans, but all are stored as strings\nin the configuration.\n"},"_lookupCacheId":{"type":"string","description":"Reference to a LookupCache resource that contains the reference data for the lookup.\n\n**Purpose**\n\nThis field connects the lookup to an external data source that has been cached in the system.\nUnlike static lookups that use the `map` property, dynamic lookups can reference large datasets\nor frequently changing information without requiring constant updates to the integration.\n\n**Implementation details**\n\nThe LookupCache resource referenced by this ID contains:\n- The data records to be used as a reference source\n- Configuration for how the data should be indexed and accessed\n- Caching parameters to balance performance with data freshness\n\n**Usage patterns**\n\nCommonly used to reference:\n- Product catalogs or SKU databases\n- Customer or account information\n- Pricing tables or discount rules\n- Complex business logic lookup tables\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n","format":"objectid"},"extract":{"type":"string","description":"JSON path expression that extracts a specific value from the cached lookup object.\n\n**Purpose**\n\nWhen using dynamic lookups with a LookupCache, this JSON path identifies which field to extract\nfrom the cached object after it has been retrieved using the lookup key.\n\n**Implementation details**\n\n- Must use JSON path syntax (similar to mapping extract fields)\n- Operates on the cached object returned by the lookup operation\n- Examples:\n  - \"$.name\" - Extract the name field from the top level\n  - \"$.details.price\" - Extract a nested price field\n  - \"$.attributes[0].value\" - Extract a value from the first element of an array\n\n**Usage scenario**\n\nWhen a lookup cache contains complex objects:\n```json\n// Cache entry for key \"PROD-123\":\n{\n  \"id\": \"PROD-123\",\n  \"name\": \"Premium Widget\",\n  \"details\": {\n    \"price\": 99.99,\n    \"currency\": \"USD\",\n    \"inStock\": true\n  }\n}\n```\n\nSetting extract to \"$.details.price\" would return 99.99 as the lookup result.\n\nIf no extract is provided, the entire cached object is returned as the lookup result.\n"},"default":{"type":["string","null"],"description":"Default value to use when the source value is not found in the lookup map.\nThe platform stores `null` here when no default is configured.\n\nThis value is used as a fallback when:\n1. The source value doesn't match any key in the map\n2. allowFailures is true or omitted\n\nSetting an appropriate default helps prevent flow failures due to unexpected\nvalues and provides predictable behavior for edge cases.\n\nCommon default patterns include:\n- Descriptive unknowns: \"Unknown Country\", \"Unspecified Status\"\n- Original value indicators: \"{Original Value}\", \"No mapping found\"\n- Neutral values: \"Other\", \"N/A\", \"Miscellaneous\"\n\nA save refuses a default together with an explicit `allowFailures: false`\n(422 `Lookups with default set can not have allowFailures explicitly set to\nfalse.`): a lookup either fails a miss (`allowFailures: false`, no default) or\nfalls back to its default.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, missing lookup values will use the default value rather than causing an error.\n\n**Behavior control**\n\nThis field determines how the system handles source values that don't exist in the map:\n\n- true: Use the default value for missing mappings and continue processing\n- false: Treat missing mappings as errors, failing the record (`lookup_error` for a\n  static map, `lookupcache_evaluation_failed` for a lookup cache)\n- omitted or null: the record doesn't fail either. On an import a static lookup writes\n  its default if it has one, and a static or cache lookup without one leaves the\n  mapped field out of the output, with no error\n\n**Recommendation**\n\nSet this to true when:\n- New source values might appear over time\n- Data quality issues could introduce unexpected values\n- Processing should continue even with imperfect mapping\n\nSet this to false when:\n- Complete data accuracy is critical\n- All possible source values are known and controlled\n- Missing mappings indicate serious data problems that should be addressed\n\nA default raises no error: records that miss go out with the default value and\nnothing reports them. Use true only when that fallback is acceptable at the\ndestination, and set false explicitly when a missing value must stop the record.\n"}}}},"Form":{"type":"object","description":"Configuration for creating user-friendly settings forms that make it easier for less technical users\nto configure integration resources.\n\n**Settings form builder**\n\nThe Settings Form Builder allows you to create or edit user-friendly fields that prompt for text entry\nor selections that will be returned as settings applied to this resource. Your forms can include any\nfield types that you see elsewhere in integrator.io, such as:\n\n- Text fields\n- Dropdown selections\n- Checkboxes\n- Radio buttons\n- Date pickers\n- Multi-select fields\n- Search fields\n\nForm fields make it much easier for less technical users to work with your integration settings by:\n\n- Providing clear labels and help text\n- Enforcing validation rules\n- Offering pre-defined selection options\n- Grouping related settings logically\n- Supporting conditional visibility\n- Creating a consistent user experience\n","properties":{"form":{"type":"object","description":"Configuration that defines the structure, fields, and behavior of the settings form.\n\nThis object contains the complete definition of the form's layout, fields, validation rules,\nand interactive behaviors. The specific structure depends on the form complexity and can include\nfield definitions, sections, conditional display logic, and default values.\n\nThe form configuration is typically created and managed through the visual Form Builder interface\nrather than edited directly as JSON.\n","properties":{"fieldMap":{"type":"object","description":"A mapping of field identifiers to their configuration objects.\nEach key in this object represents a unique field ID, and the value contains\nall the configuration settings for that specific form field.\n","additionalProperties":{"type":"object","description":"Configuration for an individual form field.\n","properties":{"id":{"type":"string","description":"Unique identifier for this field within the form: layout containers and conditions\nrefer to the field by it. Use the same string as the field's fieldMap key and its `name`.\n"},"name":{"type":"string","description":"Key the field's value is saved under: one top-level key of the resource's `settings`,\nwhich the form also reads the value back from when it opens. Nested objects inside\n`settings` are written to it directly, not through a form field's `name`.\n"},"type":{"type":"string","description":"The type of form control to render for this field.\n","enum":["text","checkbox","radiogroup","relativeuri","editor","keyvalue","select","multiselect","toggle","datetime","date","exportSelect","staticMap"]},"label":{"type":"string","description":"Display label shown next to the field in the form.\n"},"description":{"type":"string","description":"Detailed explanation text that appears below the field, providing more context\nthan the label or helpText.\n"},"helpText":{"type":"string","description":"Explanatory text that appears when hovering over the help icon next to the field.\nUsed to provide additional guidance on how to use the field.\n"},"required":{"type":"boolean","description":"When true, the field must have a value before the form can be submitted. Only the form\nenforces it: an API write of `settings` is stored without the value.\n","default":false},"defaultValue":{"description":"Value the form shows while the resource's `settings` holds no value under the field's\n`name`.\n"},"multiline":{"type":"boolean","description":"For text fields, determines whether the input should be a multi-line text area\ninstead of a single-line input.\n","default":false},"rowsMax":{"type":"integer","description":"For multiline text fields, specifies the maximum number of visible rows.\n"},"inputType":{"type":"string","description":"For text fields, specifies the HTML input type attribute to apply additional\nvalidation or specialized input behavior.\n","enum":["text","number","email","password","tel","url"]},"delimiter":{"type":"string","description":"For text fields, specifies a character to use for splitting the input into an array.\nUsed for collecting multiple values in a single text field.\n"},"mode":{"type":"string","description":"For editor fields, specifies the type of content being edited for syntax highlighting.\n","enum":["json","xml","csv","text"]},"keyName":{"type":"string","description":"For keyvalue and staticMap fields, names the first column: each saved row holds that\ncolumn's value under this key. A keyvalue field without it uses `key`.\n"},"valueName":{"type":"string","description":"For keyvalue and staticMap fields, names the second column: each saved row holds that\ncolumn's value under this key. A keyvalue field without it uses `value`.\n"},"keyLabel":{"type":"string","description":"For staticMap fields, heading of the first column.\n"},"valueLabel":{"type":"string","description":"For staticMap fields, heading of the second column.\n"},"keyOptions":{"type":"array","description":"For staticMap fields, suggestions offered in the first column's cells. A cell still\naccepts a typed value.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"Display text for the suggestion.\n"},"value":{"type":"string","description":"Value saved when the suggestion is chosen.\n"}}}},"valueOptions":{"type":"array","description":"For staticMap fields, suggestions offered in the second column's cells. A cell still\naccepts a typed value.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"Display text for the suggestion.\n"},"value":{"type":"string","description":"Value saved when the suggestion is chosen.\n"}}}},"keyResource":{"type":"object","description":"For staticMap fields, fills the first column's suggestions from an export the form\ncarries inline in `virtual`, run the way an exportSelect's `resource` is. With\n`keyResource` or `valueResource` set, the form requires both columns in every row\nunless `keyValueRequired` is false.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline, shaped like an exportSelect's `resource.virtual`.\n","additionalProperties":true}}},"valueResource":{"type":"object","description":"For staticMap fields, fills the second column's suggestions from an export the form\ncarries inline in `virtual`, run the way an exportSelect's `resource` is.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline, shaped like an exportSelect's `resource.virtual`.\n","additionalProperties":true}}},"keyValueRequired":{"type":"boolean","description":"For staticMap fields with `keyResource` or `valueResource`, when false, saves rows\nthat fill only one column; by default the form drops them.\n","default":true},"resource":{"type":"object","description":"For exportSelect fields, the export whose records become the options. When the form\nopens, the UI previews the export in `virtual` (with `test.limit` 100 unless the export\nsets its own) and lists the preview's `data`: each record needs a `label` and a `value`\n(a string or number serves as both), so shape them with the export's `transform`. A\nsaved export can't be referenced, and an HTTP export that finds no records leaves the\nfield showing an error instead of options.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline: `adaptorType`, its adaptor block, and an optional\n`transform`. `_connectionId` defaults to the connection of the export or import the\nform belongs to; set it on any other resource's form.\n","additionalProperties":true}}},"multiselect":{"type":"boolean","description":"For exportSelect fields, when true, the user can choose several options and the value\nis saved as an array.\n","default":false},"supportHandlebars":{"type":"boolean","description":"For exportSelect fields, when true, renders the `resource.virtual` export as a Handlebars\ntemplate before each preview (all but its `transform`, `hooks`, and `filter`). On an\nintegration, flow group, flow, export, or import form, the form's current values are\nunder that form's scope name (`integration`, `flowGrouping`, `flow`, `export`, or\n`import`), beside the saved settings of the scopes above it, with no `settings.`\nprefix; on a connection or iClient form the template has no context. The options then\nload through `refreshOptionsOnChangesTo` or `typeAheadSearch`, or when the user\nrefreshes the field.\n","default":false},"refreshOptionsOnChangesTo":{"type":"array","items":{"type":"string"},"description":"For exportSelect fields with `supportHandlebars`, ids of the fields whose values the\ntemplated export uses: the options load once one of them has a value and reload whenever\none changes.\n"},"typeAheadSearch":{"type":"boolean","description":"For exportSelect fields, when true, re-runs the templated export as the user types, with\nthe typed text as this field's own value; an empty box clears the options, and typed\ntext is accepted as the value.\n","default":false},"showDelete":{"type":"boolean","description":"For keyvalue fields, determines whether to show a delete button for each key-value pair.\n"},"doNotAllowFutureDates":{"type":"boolean","description":"For date and datetime fields, restricts selection to dates not in the future.\n"},"skipTimezoneConversion":{"type":"boolean","description":"For datetime fields, prevents automatic timezone conversion of the date value.\n"},"options":{"type":"array","description":"For fields that present choices (select, multiselect, radiogroup, toggle), defines\nthe available options.\n","items":{"anyOf":[{"title":"Option group","type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"oneOf":[{"title":"String value","type":"string"},{"title":"Label-value pair","type":"object","properties":{"label":{"type":"string","description":"Display text for the option.\n"},"value":{"type":"string","description":"Value to store when this option is selected.\n"}}}]},"description":"Array of option values/labels to display in the selection control.\n"}}},{"title":"Label-value pair","type":"object","required":["label"],"properties":{"label":{"type":"string","description":"Display text for the option.\n"},"value":{"type":"string","description":"Value to store when this option is selected.\n"}}}]}},"visibleWhen":{"type":"array","description":"Conditional display rules that determine when this field should be visible.\nIf empty or not provided, the field is always visible.\n","items":{"type":"object","properties":{"field":{"type":"string","description":"The ID of another field whose value controls the visibility of this field. The\nsave doesn't check it: a rule naming an ID that isn't in the form counts as met,\nso the field stays visible.\n"},"is":{"type":"array","items":{"type":["string","boolean","number","null"]},"description":"Array of values - if the referenced field has any of these values,\nthis field will be visible. Values may be strings, booleans (for\ncheckbox/toggle fields), numbers (for numeric inputs), or null.\n"}}}}}}},"layout":{"type":"object","description":"Defines how the form fields are arranged and grouped in the UI.\nThe layout can organize fields into columns, sections, or other visual groupings.\n","properties":{"type":{"type":"string","description":"The type of layout to use for the form.\n","enum":["column","collapse","box","indent","tabWithoutSave","verticalTabWithoutSave"]},"fields":{"type":"array","items":{"type":"string"},"description":"Field IDs shown above the containers, without a heading.\n"},"containers":{"type":"array","description":"Array of container objects that group fields or contain nested containers.\nEach container can represent a column, box, indented section, or collapsible section.\n","items":{"type":"object","properties":{"type":{"type":"string","description":"The visual style of the container.\n","enum":["indent","box","collapse"]},"label":{"type":"string","description":"The heading text displayed for this container.\n"},"fields":{"type":"array","items":{"type":"string"},"description":"Array of field IDs that should be displayed in this container.\nEach ID must correspond to a key in the fieldMap object: the save doesn't check it,\nand an ID that doesn't renders nothing. A field the layout doesn't list isn't shown.\n"},"containers":{"type":"array","description":"Nested containers within this container. Allows for hierarchical organization\nof fields with different visual styles.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"The heading text displayed for this nested container.\n"},"fields":{"type":"array","items":{"type":"string"},"description":"Array of field IDs that should be displayed in this nested container.\n"}}}}}}}}}},"additionalProperties":true},"init":{"type":"object","description":"Configuration for custom JavaScript initialization that executes when the form is first loaded.\n\nThis object defines a JavaScript hook that prepares the form for use, sets initial field values,\nperforms validation, or otherwise customizes the form behavior before it is displayed to the user.\n\n**Function signature**\n\nThe initialization function is invoked with a single 'options' argument containing contextual information:\n```javascript\nfunction formInit(options) {\n  // Process options and return the form object\n  return options.resource.settingsForm.form;\n}\n```\n\n**Available context**\n\nThe 'options' argument provides access to:\n- `options.resource` - The current resource being configured\n- `options.parentResource` - The parent of the current resource\n- `options.grandparentResource` - The grandparent of the current resource\n- `options.license` - For integration apps, the license provisioned to the integration\n- `options.parentLicense` - For integration apps, the parent of the license\n- `options.sandbox` - Boolean flag indicating whether the script is running in a sandbox environment\n\n\n**Common uses**\n\n- Dynamically generate field options based on resource configuration\n- Pre-populate default values from related resources\n- Apply conditional logic that depends on resource properties\n- Add, remove, or modify form fields based on user permissions or account settings\n- Fetch external data to populate selection options\n- Implement complex validation rules that depend on resource context\n- Create branching form experiences based on user selections\n\n**Return value**\n\nThe function must return a valid form object that the UI can render.\nThrowing an exception will signal an error to the user.\n\nThe UI runs the function each time it opens the form and renders the returned form instead of\n`form`, then fills in the values from `settings`; a thrown error's message replaces the form.\n","properties":{"function":{"type":"string","description":"The name of the function to execute within the referenced script.\n\nThis property specifies which function to invoke from the script\nreferenced by _scriptId. The function will be called when the form\nis initialized and should handle any custom setup logic.\n\nThe function must follow the expected signature and return a valid form object.\n"},"_scriptId":{"type":"string","format":"objectId","description":"Reference to a predefined script resource containing the initialization function.\n\nThe referenced script should contain the function specified in the\n'function' property. On an integration, a flow group, a flow, an export, an import, or a\nconnection, an id that isn't a script in the same account (or, on an Integration App's\nresources, one the app shares) is refused with 422 `invalid_ref`. An iClient save\nstores the id without that check.\n"}}}}},"Settings":{"type":"object","description":"Configuration settings that can be accessed by hooks, filters, mappings and handlebars templates at runtime.\n\nIt enables customization of the resource's logic, allowing hooks, mappings, filters, and\nhandlebars to access and apply the settings at runtime.\n\n**Usage**\n\nThe settings object can store arbitrary JSON data that you want to save with the resource.\nWhile it's often populated through a form defined in the `settingsForm` field, you can also:\n\n- Directly provide JSON settings without using a form\n- Store configuration values used by hooks and templates\n- Create resource-specific constants and parameters\n- Maintain lookup tables or mapping structures\n- Define conditional logic parameters\n\n**Accessibility**\n\nSettings are available in:\n- All handlebars fields for building dynamic payloads\n- Field mapping expressions\n- JavaScript hooks via the options object\n- Filters and transformations\n\nAt runtime, the settings objects in the step's execution chain are gathered into a\nsingle `settings` context keyed by fixed scope keys. Flow runs populate the full\nchain: `settings.integration.*`, `settings.flowGrouping.*` (the flow group the flow\nbelongs to), `settings.flow.*`, `settings.connection.*`, `settings.iClient.*`, and\nthe running step's own scope — `settings.export.*` on exports/lookups,\n`settings.import.*` on imports (the key matches the step type). Steps executing\ninside a My API or a Tool receive only the step's own scope plus\n`settings.connection.*` / `settings.iClient.*`; the integration, flowGrouping, and\nflow scopes resolve empty there. The scope key is literal — resource display names\nand settingsForm section labels are never part of the path. The segments after the\nscope mirror the stored settings JSON key path exactly: a top-level field is\n`settings.<scope>.<fieldId>`, and nested objects add one segment per JSON key\n(e.g. `settings.flowGrouping.Customer.region` when the group's settings JSON nests\n`region` under `Customer`). References without a scope key\n(e.g. `{{settings.myField}}`) resolve to empty strings.\n\n**Best practices**\n\nFor non-technical users, create a custom form instead of editing the JSON directly.\nThis provides a user-friendly interface for updating settings without requiring JSON knowledge.\n","additionalProperties":true},"MockOutput":{"type":["object","null"],"description":"Sample data that simulates the output from an export for testing and configuration purposes.\n\nMock output allows you to configure and test flows without executing the actual export or\nwaiting for real-time data to arrive. This is particularly useful for:\n- Initial flow configuration and testing\n- Mapping development without requiring live data\n- Generating metadata for downstream flow steps\n- Creating realistic test scenarios\n- Documenting expected data structures\n\n**Structure**\n\nThe mock output must follow the integrator.io canonical format, which consists of a\n`page_of_records` array containing record objects. Each record object has a `record`\nproperty that contains the actual data fields.\n\n```json\n{\n  \"page_of_records\": [\n    {\n      \"record\": {\n        \"field1\": \"value1\",\n        \"field2\": \"value2\",\n        ...\n      }\n    },\n    ...\n  ]\n}\n```\n\n**Usage**\n\nIn a flow test run, and for an upstream lookup during a page-processor preview\n(`previewPageProcessor`), integrator.io substitutes this mock output for the export's live\nresponse instead of calling the source. A saved mock is not read anywhere else: a target\nlookup in that preview runs its live query, invoking the export\n(`POST /v1/exports/{_id}/invoke`) reads the source, and real runs ignore it. This allows you\nto:\n- Test mappings with representative data\n- Configure downstream flow steps without waiting for real data\n- Simulate various data scenarios\n\nWhere it is substituted, the records stand in for the RAW parsed response: the export's own\n`transform` and output `filter` still run on top of them, so downstream steps — and a\nlookup's results mapping — see the transformed, filtered records, not the shape stored here.\n\nIn a flow test run of an HTTP export with a saved mock (live probe, 2026-10-21), the export\nstill listed `request` and `response` stages: `request` held the request the export composes,\nand `response` held the mock under the source's URL, as did the test run's request log, which\nrecorded it as a 200. Read those stages as the substituted mock, not as a live call.\n\n`POST /v1/exports/preview` reads the `mockOutput` in its request body, not the saved one. For\nan export the preview supports (an HTTP export, for example), a body that carries it (as an\nexport read back with GET does) returns that snapshot as a lone `parse` stage without calling\nthe source; remove `mockOutput` from the body to preview live records.\n\n**Limitations**\n\n- Maximum of 10 records\n- Maximum size of 1 MB\n- Must follow the canonical format shown above\n\nMock output can be populated automatically from preview data (which is the parsed response,\nbefore `transform` / `filter`) or entered manually.\n","properties":{"page_of_records":{"type":"array","description":"Array of record objects in the integrator.io canonical format.\n\nEach item in this array represents one record that would be processed\nby the flow during execution.\n","items":{"type":"object","properties":{"record":{"type":"object","description":"Container for the actual record data fields.\n\nThe structure of this object will vary depending on the specific\nexport configuration and the source system's data structure.\n","additionalProperties":true}}}}}},"PreSave":{"type":"object","description":"Defines a JavaScript hook that executes before the resource is saved.\n\nThis hook allows for programmatic validation, transformation, or enrichment of the\nresource itself before it is persisted. It can be used to enforce business rules,\nset derived properties, or implement cross-field validations that can't be expressed\nthrough the standard UI.\n\n**Function signature**\n\nThe preSave function is invoked with a single 'options' argument containing:\n```javascript\nfunction preSave(options) {\n  // Process options and return the modified resource\n  return options.newResource;\n}\n```\n\n**Available context**\n\nThe 'options' argument provides access to:\n- `options.newResource` - The resource being saved (with pending changes)\n- `options.oldResource` - The previous version of the resource (before changes)\n- `options.sandbox` - Boolean flag indicating whether the script is running in a sandbox environment\n\n\n**Common uses**\n\n- Enforcing complex business rules across multiple fields\n- Automatically deriving field values based on other configuration\n- Performing validation that depends on external systems or data\n- Normalizing or standardizing configuration values\n- Adding computed or derived properties\n- Implementing versioning or change tracking\n- Dynamically looking up data using the Celigo API module to enrich configuration\n\n**Return value**\n\nThe function must return the newResource object (potentially modified) to be saved.\nThrowing an exception will prevent saving and signal an error to the user.\n","properties":{"function":{"type":"string","description":"The name of the function to execute within the referenced script.\n\nThis property specifies which function to invoke from the script\nreferenced by _scriptId. The function will be called just before\nthe resource is saved.\n\nThe function must follow the expected signature and return the resource object.\n"},"_scriptId":{"type":"string","format":"objectId","description":"Reference to a predefined script resource containing the preSave function.\n\nThe referenced script should contain the function specified in the\n'function' property. This script must be accessible within the user's account\nand have appropriate permissions.\n"}}},"ResourceResponse":{"type":"object","description":"Response","properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Unique identifier for the resource. Format is a 24-character hexadecimal string."},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was created. Set automatically and cannot be modified."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was last updated. Changes whenever any property is modified."},"deletedAt":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp when the resource was soft-deleted. When null or absent, the resource is active."}},"required":["_id"]},"IAResourceResponse":{"type":"object","description":"Integration app response fields for resources that are part of integration apps","properties":{"_integrationId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the specific integration instance that contains this resource.\n\nThis field is only populated for resources that are part of an integration app\ninstallation. It contains the unique identifier (_id) of the integration\nresource that was installed in the account.\n\nThe integration instance represents a specific installed instance of an\nintegration app, with its own configuration, settings, and runtime environment.\n\nThis reference enables:\n- Tracing the resource back to its parent integration instance\n- Permission and access control based on integration ownership\n- Lifecycle management (enabling/disabling, updating, or uninstalling)\n"},"_connectorId":{"type":"string","format":"objectId","readOnly":true,"description":"Integration App (connector) that defines this resource. Present only on resources an\nIntegration App owns. The app locks parts of these documents, and a PUT that changes a\nlocked field answers 200 but keeps the stored value, so read the document back to confirm\na change."}}},"AIDescription":{"type":"object","description":"AI-generated descriptions and documentation for the resource.\n\nThis object contains automatically generated content that helps users\nunderstand the purpose, behavior, and configuration of the resource without\nrequiring them to analyze the technical details. The AI-generated content\nis sanitized and safe for display in the UI.\n","properties":{"summary":{"type":["string","null"],"description":"Brief AI-generated summary of the resource's purpose and functionality.\n\nThis concise description provides a quick overview of what the resource does,\nwhat systems it interacts with, and its primary role in the integration.\nThe summary is suitable for display in list views, dashboards, and other\ncontexts where space is limited.\n\nMaximum length: 10KB\n"},"detailed":{"type":["string","null"],"description":"Comprehensive AI-generated description of the resource's functionality.\n\nThis detailed explanation covers the resource's purpose, configuration details,\ndata flow patterns, filtering logic, and other technical aspects. It provides\nin-depth information suitable for documentation, tooltips, or detailed views\nin the administration interface.\n\nThe content may include HTML formatting for improved readability.\n\nMaximum length: 10KB\n"},"generatedOn":{"type":["string","null"],"format":"date-time","description":"Timestamp indicating when the AI description was generated.\n\nThis field helps track the freshness of the AI-generated content and\ndetermine when it might need to be regenerated due to changes in the\nresource's configuration or behavior.\n\nThe timestamp is recorded in ISO 8601 format with UTC timezone (Z suffix).\n"}}},"APIM":{"type":"array","description":"Read-only field that stores information about the integration resources\npublished in the API Management (APIM) platform.\n\nThis field tracks the relationship between integrator.io resources and their\npublished counterparts in the APIM platform, which is\ntightly integrated with the Celigo UI. When resources are \"pushed\" to APIM,\nthis field is populated with the relevant identifiers and statuses.\n","items":{"type":"object","properties":{"apiId":{"type":"string","description":"Identifier for the API where this integrator.io resource is published in the APIM.\n\nThis is an APIM resource identifier (not prefixed with underscore like Celigo IDs)\nthat uniquely identifies the API in the API Management platform.\n"},"flowId":{"type":"string","description":"Identifier for the flow within the API where this integrator.io resource is linked.\n\nWhen an API has multiple integrator.io resources linked, each resource is associated\nwith a specific flow in the API, identified by this field. This is an APIM\nresource identifier.\n"},"status":{"type":"string","description":"Indicates the publishing stage of the integrator.io resource in APIM.\n\nPossible values:\n- 'oaspending': The resource is published but the OpenAPI Specification (OAS) is not\n  yet published. The apiId will be updated with the API ID created in APIM.\n- 'published': The OpenAPI Specification for the integrator.io resource has been\n  successfully uploaded to APIM.\n","enum":["oaspending","published"]}}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/exports/{_id}":{"get":{"summary":"Get an export","description":"Returns the complete configuration of a specific export.\n","operationId":"getExportById","tags":["Exports"],"parameters":[{"name":"_id","in":"path","description":"The unique identifier of the export","required":true,"schema":{"type":"string","format":"objectId"}}],"responses":{"200":{"description":"Export retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Export"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
````

## Update an export

> Updates an existing export with the provided configuration.\
> This is used for major updates to an export's structure or behavior.<br>

````json
{"openapi":"3.2.0","info":{"title":"Exports","version":"1.0.0"},"tags":[{"name":"Exports","description":"Exports retrieve data from source systems — on a schedule, in delta mode, in real time,\non demand, or as a file/blob transfer — package the results into ≤ 5-MB pages, and pass\neach page to downstream flow steps. Depending on configuration, an export surfaces in the\nFlow Builder as an export, a real-time listener, a file transfer, or a mid-flow lookup.\n\n## Export schema\n\n{% openapi-schemas spec=\"export\" schemas=\"Export\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"Request":{"type":"object","description":"Fields that can be sent when creating or updating an export. Set the adaptor-specific\nconfiguration object matching `adaptorType`, and the mode-specific object matching `type`.\n`SimpleExport` (data-loader) is the exception — it needs no adaptor config object. An Event\nStreams listener (`TopicExport`) carries `topic` and `type: stream` and no `_connectionId`.","required":["name"],"allOf":[{"$ref":"#/components/schemas/ExportBase"}],"if":{"properties":{"adaptorType":{"const":"HTTPExport"}},"required":["adaptorType"]},"then":{"required":["http"],"if":{"properties":{"assistant":{"const":"azuredatalakestoragegen2"}},"required":["assistant"]},"then":{"required":["file"],"properties":{"file":{"required":["directory"],"properties":{"directory":{"required":["name"]}}}}},"else":{"if":{"properties":{"assistant":{"const":"googlecloudstorage"}},"required":["assistant"]},"then":{"required":["file"],"properties":{"file":{"required":["directory"],"properties":{"directory":{"required":["storageRootId","id"]}}}}},"else":{"if":{"required":["http"],"properties":{"http":{"required":["type"],"properties":{"type":{"const":"file"}},"not":{"required":["formType"],"properties":{"formType":{"const":"assistant"}}}}}},"then":{"required":["file"]}}}},"else":{"if":{"properties":{"adaptorType":{"const":"FTPExport"}},"required":["adaptorType"]},"then":{"required":["ftp"]},"else":{"if":{"properties":{"adaptorType":{"const":"AS2Export"}},"required":["adaptorType"]},"then":{"if":{"not":{"properties":{"type":{"const":"webhook"}},"required":["type"]}},"then":{"required":["file"]}},"else":{"if":{"properties":{"adaptorType":{"const":"S3Export"}},"required":["adaptorType"]},"then":{"required":["s3"]},"else":{"if":{"properties":{"adaptorType":{"const":"NetSuiteExport"}},"required":["adaptorType"]},"then":{"required":["netsuite"]},"else":{"if":{"properties":{"adaptorType":{"const":"NetSuiteHTTPExport"}},"required":["adaptorType"]},"then":{"required":["http","nsDomainType"]},"else":{"if":{"properties":{"adaptorType":{"const":"SalesforceExport"}},"required":["adaptorType"]},"then":{"required":["salesforce"]},"else":{"if":{"properties":{"adaptorType":{"const":"JDBCExport"}},"required":["adaptorType"]},"then":{"required":["jdbc"]},"else":{"if":{"properties":{"adaptorType":{"const":"RDBMSExport"}},"required":["adaptorType"]},"then":{"required":["rdbms"]},"else":{"if":{"properties":{"adaptorType":{"const":"MongodbExport"}},"required":["adaptorType"]},"then":{"if":{"properties":{"type":{"const":"stream"}},"required":["type"]},"then":{"required":["cdc"]},"else":{"required":["mongodb"],"properties":{"mongodb":{"required":["method","collection"]}}}},"else":{"if":{"properties":{"adaptorType":{"const":"DynamodbExport"}},"required":["adaptorType"]},"then":{"required":["dynamodb"]},"else":{"if":{"properties":{"adaptorType":{"const":"WrapperExport"}},"required":["adaptorType"]},"then":{"required":["wrapper"]},"else":{"if":{"properties":{"adaptorType":{"const":"WebhookExport"}},"required":["adaptorType"]},"then":{"required":["webhook"]},"else":{"if":{"properties":{"adaptorType":{"const":"FileSystemExport"}},"required":["adaptorType"]},"then":{"required":["filesystem"]},"else":{"if":{"properties":{"adaptorType":{"const":"RESTExport"}},"required":["adaptorType"]},"then":{"required":["http"]},"else":{"if":{"properties":{"adaptorType":{"const":"TopicExport"}},"required":["adaptorType"]},"then":{"required":["topic"],"properties":{"topic":{"required":["_topicId"]}}}}}}}}}}}}}}}}}}},"ExportBase":{"type":"object","description":"Writable export fields shared by the request and response schemas.","properties":{"name":{"type":"string","maxLength":100,"description":"Display name for the export, shown in the flow builder, job history, and error logs.\nDescriptive, unique names indicating the data source and purpose make large accounts easier to manage."},"description":{"type":["string","null"],"description":"Free-text summary of what the export retrieves and why. Shown in the UI and available\nto AI agents for context; has no effect on execution.","maxLength":5120},"_connectionId":{"format":"objectId","type":["string","null"],"description":"Connection this export uses to reach the source system. The connection's type must be\ncompatible with the export's `adaptorType` (e.g. an `HTTPExport` needs an `http` connection;\n`NetSuiteExport` and `NetSuiteHTTPExport` both need a `netsuite` connection).\nServer-required unless `type` is `webhook` or `simple` (those receive data instead of\nfetching it) or `adaptorType` is `TopicExport` (Event Streams listeners are delivered\nby the platform itself and must omit it). A `WebhookExport` saved without `type: webhook`\nis refused with 422 \"_connectionId is required unless type is [webhook|simple]\"; a webhook\nexport that does carry a connection saves and updates normally."},"adaptorType":{"type":"string","description":"Selects the adaptor technology that executes this export, which determines the compatible\nconnection types and which adaptor-specific configuration object must also be supplied\n(e.g. set `salesforce` when using `SalesforceExport`).","enum":["HTTPExport","FTPExport","AS2Export","S3Export","NetSuiteExport","NetSuiteHTTPExport","SalesforceExport","JDBCExport","RDBMSExport","MongodbExport","DynamodbExport","WrapperExport","SimpleExport","WebhookExport","FileSystemExport","TopicExport","RESTExport"]},"nsDomainType":{"type":"string","enum":["suitetalk","restlet"],"description":"Selects which NetSuite REST host a `NetSuiteHTTPExport` calls. The server derives the\nfull base URL from the connection's NetSuite account\n(`https://<account>.suitetalk.api.netsuite.com` or `https://<account>.restlets.api.netsuite.com`)\nand signs each request with the connection's token-based credentials, so `http.relativeURI`\ncarries the complete path starting at `/services/rest/...` or `/app/site/hosting/restlet.nl`.\nIgnored on other adaptor types."},"type":{"type":["string","null"],"description":"Operational mode of the export. When omitted, the export retrieves all available records —\nthe standard batch mode, which is also the right choice for parsing structured files\n(CSV/XML/JSON) into records. Most modes take their matching configuration object (e.g. a\n`webhook` object when `type` is `webhook`); an HTTP or SQL `delta` export saves without a\n`delta` object because its request carries `{{lastExportDateTime}}`, though\n`delta.startDate` sets its first window. It still needs `type: delta`: on an export\nwith no `type`, `{{lastExportDateTime}}` and `{{currentExportDateTime}}` render\nempty on every run, so a request that carries them goes out with blank values each time\n(live-verified 2026-10-22). Use `blob` only to transfer raw files without parsing their\ncontents.","enum":["webhook","test","delta","once","tranlinedelta","simple","blob","distributed","stream","all"]},"pageSize":{"type":["integer","null"],"description":"Number of records per page streamed to downstream flow steps when the export is the\nflow's source step. Pages are additionally capped at 5 MB regardless of record count, so\nvery large records may produce smaller pages. The server does not validate the value.\nA lookup (`isLookup: true`) runs to completion and hands every result to the incoming\nrecord as one response, capped at 5 MB in total, so the value does not change how many\nresults a lookup returns; a result set larger than that belongs in a standalone export\nstep. In a test run or preview the value also caps the records fetched (default 20).\nOn a webhook listener the records of each incoming request are paged separately, before\nany hook or downstream step runs.","default":20},"dataURITemplate":{"type":"string","description":"Handlebars template that builds a link back to each record in the source application's UI\n(e.g. `https://my.salesforce.com/lightning/r/Contact/{{record.Id}}/view`). The resolved\nURL is stored with error records in job history so users can jump straight to the record."},"traceKeyTemplate":{"type":["string","null"],"description":"Handlebars template that overrides how each record's unique trace key is generated, used\nto track records through the flow and match retries to prior errors (e.g.\n`{{join \"_\" record.customerId record.orderId}}`). When omitted, the system picks a unique\nfield automatically from the record's top level, by exact field name (`GET\n/v1/flows/{_id}/traceKey` serves the pattern). The rendered value of a template is stamped\non each record as `io_custom_trace_key` and travels downstream with it. If a transform\nreshapes the data first, omit the `record.` prefix. Trace keys are capped at 256 characters;\na longer key is stored truncated from the middle, keeping the beginning and end of the\nvalue. Downstream imports use the trace key as their default concurrency lock key (a blank\nkey means no lock); it never de-duplicates — a record emitted twice with the same key is\nprocessed twice."},"skipRetries":{"type":"boolean","description":"When true, the platform does not retain the source data needed to retry failed records,\nreducing stored data at the cost of being unable to reprocess errors from this export.\nShown as \"Do not store retry data\" in the UI. Defaults to false."},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"isLookup":{"type":"boolean","description":"When true, the export runs as a mid-flow lookup: it executes once per incoming record,\nusing the input record's fields to parameterize the request, and passes the results to\nsubsequent steps. When false, the export runs as a standalone data source. A lookup's\nresults are not paged through the flow: every page the adaptor fetches is merged into one\nresponse for the record, up to 5 MB, so `pageSize` does not limit or extend what a lookup\nreturns. A lookup that must return more than that is better modeled as a standalone\nexport step."},"groupByFields":{"$ref":"#/components/schemas/GroupBy"},"delta":{"$ref":"#/components/schemas/Delta"},"test":{"$ref":"#/components/schemas/Test"},"once":{"$ref":"#/components/schemas/Once"},"webhook":{"$ref":"#/components/schemas/Webhook"},"simple":{"$ref":"#/components/schemas/Simple"},"distributed":{"$ref":"#/components/schemas/Distributed"},"cdc":{"$ref":"#/components/schemas/Cdc"},"topic":{"$ref":"#/components/schemas/Topic"},"filesystem":{"$ref":"#/components/schemas/FileSystem"},"http":{"$ref":"#/components/schemas/Http"},"file":{"$ref":"#/components/schemas/File"},"salesforce":{"$ref":"#/components/schemas/Salesforce"},"as2":{"$ref":"#/components/schemas/AS2"},"dynamodb":{"$ref":"#/components/schemas/DynamoDB"},"ftp":{"$ref":"#/components/schemas/FTP"},"jdbc":{"$ref":"#/components/schemas/JDBC"},"mongodb":{"$ref":"#/components/schemas/MongoDB"},"netsuite":{"$ref":"#/components/schemas/NetSuite"},"rdbms":{"$ref":"#/components/schemas/RDBMS"},"s3":{"$ref":"#/components/schemas/S3"},"wrapper":{"$ref":"#/components/schemas/Wrapper"},"parsers":{"$ref":"#/components/schemas/Parsers"},"filter":{"description":"Filter applied to each record after the export's transform and before its preSavePage\nhook. Records that match continue through the flow; records that don't are silently\ndropped. Filter expressions reference the transformed record's fields.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"inputFilter":{"description":"Filter applied to incoming records before a lookup export queries the external system.\nOnly matching input records trigger lookup calls; non-matching records pass through the\nstep unenriched, which cuts unnecessary API traffic. Only relevant when `isLookup` is true.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"mappings":{"description":"Field mappings applied to each incoming record before a lookup HTTP request is made,\nreshaping the upstream record into the structure the lookup target API expects. Only\nsupported when `isLookup` is true and `adaptorType` is `HTTPExport` or\n`NetSuiteHTTPExport`; do not set it on source exports or other lookup adaptors.","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"transform":{"description":"Transformation that reshapes records before they leave this step — source exports\ntransform the retrieved records, lookup exports transform the lookup results. Commonly\nused to flatten nested lookup responses (e.g. `results[0].id` → `id`); merging results\nback into source records is handled separately by the flow's response mapping.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the export lifecycle for\ntransformations, validation, and filtering beyond what configuration alone can express.\nA hook name not listed here (an import's `preMap`, for example) is dropped on save\nwithout an error, so read `hooks` back after saving.","properties":{"preSavePage":{"type":"object","description":"Hook that runs on each page of records after the export's transform and output\nfilter, before the page is sent to downstream steps; commonly used to flatten\nstructures, drop unwanted records, or add computed fields. It receives the page as\n`options.data` (with `errors`, `pageIndex`, `configuration` and `settings`) and returns\n`{data, errors, abort, newErrorsAndRetryData}`. Return that object, not the array: a\nhook that returns `options.data` itself, or a filtered copy, drops the page without an\nerror, and the run completes with 0 records, 0 errors and 0 pages. An exception thrown\nhere fails every record on that page, each with its own retry data, and later pages\nstill run.","properties":{"function":{"type":"string","description":"Function to invoke within the referenced script."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`. Must be a script in this\naccount: any other id fails the save with 422 (\"Only a script owned by the user or\na script shared by the Integration App can be used at hooks.preSavePage._scriptId.\")."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments."},"configuration":{"type":"object","description":"Static parameters passed to the hook function at runtime, letting one script be\nreused across exports with different settings."}}},"preParse":{"type":"object","description":"Hook that runs on file-based exports after the file is downloaded and before it is\nparsed: it receives the raw file text as `preParseData` together with `fileMeta` and\nreturns both, modified or not, for the configured parser to read. Used to normalize a\nnon-standard file — fix a header row, strip a preamble, convert a format — before\nparsing; an exception thrown here fails the run.","properties":{"function":{"type":"string","description":"Function to invoke within the referenced script."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments."},"configuration":{"type":"object","description":"Static parameters passed to the hook function at runtime, letting one script be\nreused across exports with different settings."}}}}},"settingsForm":{"$ref":"#/components/schemas/Form"},"settings":{"$ref":"#/components/schemas/Settings"},"mockOutput":{"$ref":"#/components/schemas/MockOutput"},"_ediProfileId":{"type":"string","format":"objectId","description":"EDI profile this export uses to parse incoming X12 or EDIFACT documents into structured\nJSON — it supplies the envelope qualifiers, delimiters, version, and validation rules.\nSet it when the export parses or validates EDI files (a save with a `delimited/x12` file\ndefinition at `version` `\"2\"` and no profile fails with 422 `missing_ediProfile`); omit\nit otherwise. Accepted on the file-reading exports — FTP, AS2, S3 and HTTP file mode\n(`http.type: \"file\"`)."},"_postParseListenerId":{"type":"string","format":"objectId","description":"Webhook export invoked once per file after EDI parsing, on both success and failure\n(errors are included in the payload when parsing fails). Primarily used to send\nfunctional acknowledgements (997/999) to trading partners. Accepted on the file-reading\nexports that parse EDI — FTP, AS2, S3 and HTTP file mode (`http.type: \"file\"`)."},"externalId":{"type":["string","null"],"description":"External identifier for correlating the export with a record in another system."},"_integrationId":{"type":["string","null"],"format":"objectId","description":"Not stored on an export: a value sent on create or on a full update is dropped, and a GET\ndoesn't return the field. An export isn't owned by an integration; to find the flows and\nother resources that use it, call `GET /v1/exports/{_id}/dependencies`."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this export was created from, set when the export is part of an installed\nintegration app. The app's install sets it: a create that sends it is refused with 403\n(\"Cannot create an Integration App resource\")."},"unencrypted":{"type":"object","description":"Custom configuration values stored without encryption and returned in API responses."},"useTechAdaptorForm":{"type":"boolean","description":"When true, the UI presents the full technical adaptor form for this export instead of\nthe simplified assistant form."},"rawData":{"type":"string","description":"Key referencing the raw sample payload captured for preview and testing."},"preSave":{"$ref":"#/components/schemas/PreSave"},"assistant":{"type":"string","description":"Identifier for the connector assistant used to configure this export. For the cloud\nfile providers it also selects the provider-specific location rules —\n`azuredatalakestoragegen2` (container in `file.directory.name`, path in\n`http.relativeURI`) and `googlecloudstorage` (bucket in `file.directory.storageRootId`,\nprefix in `file.directory.id`)."},"assistantMetadata":{"type":["object","string"],"additionalProperties":true,"description":"Metadata associated with the connector assistant configuration."},"sampleData":{"type":["string","object","array","null"],"description":"Sample data payload used for previewing and testing the export."},"sampleHeaders":{"type":"array","description":"Sample HTTP headers used for previewing and testing the export.","items":{"type":"object","properties":{"name":{"type":"string","description":"Header name."},"value":{"type":"string","description":"Header value."}}}},"sampleQueryParams":{"type":"array","description":"Sample query parameters used for previewing and testing the export.","items":{"type":"object","properties":{"name":{"type":"string","description":"Query parameter name."},"value":{"type":"string","description":"Query parameter value."}}}}}},"OneToMany":{"type":"boolean","description":"When true, the step runs once per child record instead of once per incoming record.\n`pathToMany` names the array field that holds the children inside an object record; when\nthe incoming record is itself an array (grouped or row-based data), leave `pathToMany` blank\nand each element becomes a record. The fan-out is scoped to this step: afterwards the children\nare re-joined into the record's original shape — the object with its array, or the array of rows —\ncarrying any response-mapping enrichment, and that re-joined record is what the next step\nreceives. Not for locating records in an export's HTTP response; use\n`http.response.resourcePath` for that. Applies to steps that receive a record — imports and\nlookups (an export referenced as a page processor of a flow, API, or Tool). A standalone export\n(a flow's page generator) has no incoming record, so the fields are saved but have no effect on\nthe records it produces; to emit one record per array element from a standalone export, use a\n`hooks.preSavePage` script, or for a file-definition export make the repeating segment the\nrecord boundary in the definition's rules.\n","default":false},"PathToMany":{"type":"string","description":"Path to the array of child records inside an object record when `oneToMany` is true, in dot\nnotation (`items`, `lines.lineItems`). Leave blank when the incoming record is itself an array\n(grouped or row-based data) — each element is then a child record. A path that does not resolve\nto an array processes zero records and reports success. Read only on steps that receive a\nrecord (imports, lookup exports); on a standalone export it is saved and ignored — see `oneToMany`.\n"},"GroupBy":{"type":"array","description":"Specifies which fields to use for grouping records in the export results. When configured, records with\nthe same values in these fields will be grouped together and treated as a single record by downstream\nsteps in your flow.\n\nFor example:\n- Group sales orders by customer ID to process all orders for each customer together\n- Group journal entries by accounting period to consolidate related transactions\n- Group inventory items by location to process inventory by warehouse\n\nWhen grouping is used, the export's page size determines the maximum number of groups per page, not individual\nrecords. Note that effective grouping typically requires that records with the same group field values appear\ntogether in the export data.\n\nNot a webhook listener feature: on a `webhook` export the platform applies it only within each page, after\nthe request has been paged, and only to adjacent rows — the field is not offered for listeners in the UI.\n","items":{"type":"string"}},"Delta":{"type":"object","description":"Configures incremental exports that retrieve only records created or modified since the\nflow's delta checkpoint; omit it for other export types. Adaptors that compare a record field\nname it in `dateField`, while HTTP exports and SQL queries carry {{lastExportDateTime}} in the\nrequest and save without a delta object. Each run's window runs from {{lastExportDateTime}}\n(the checkpoint) to {{currentExportDateTime}} (the time the run started). A first run is not a\nfull export: with no `startDate`, its {{lastExportDateTime}} is the run's own timestamp, so it\npicks up no earlier changes. The checkpoint moves to a run's window end only when the run\nexports records, so a run that exports nothing leaves it where it was, and after a failed run\nthe next run reuses the last checkpoint. On success, when `POST /v1/flows/{_id}/run` sends\n`export.endDate`, the flow's checkpoint is left at that value, so an end date in the past\nmoves the next run's lower bound back.","properties":{"dateField":{"type":"string","description":"Record timestamp field(s) compared against the last successful run time to identify\nchanged records. Accepts a single field or multiple comma-separated fields, processed\nsequentially — useful when different operations update different timestamp fields.\nIf the flow's own downstream steps update the exported records, add export criteria\nthat exclude already-processed records (or use a creation-time field for process-once\nflows) so each run doesn't re-export what the previous run wrote. Not supported on\nHTTP exports: embed {{lastExportDateTime}} in the relativeURI or body instead; an HTTP\nexport saved with dateField is refused with 422 \"Unsupported dateField in\nexport.delta.dateField\". For Salesforce this\nfield is required and defaults to the standard timestamp fields (LastModifiedDate,\nCreatedDate, SystemModstamp, LastActivityDate, LastViewedDate, LastReferencedDate) plus\nany custom timestamp fields."},"dateFormat":{"type":"string","description":"Moment.js format string applied to both delta variables, {{lastExportDateTime}} and\n{{currentExportDateTime}}, in HTTP requests and SQL queries alike. Leave unset unless the\nsource system requires a non-ISO8601 format; by default both render as ISO 8601 UTC with\nmilliseconds. Date-only formats truncate the time portion, widening the filter window."},"lagOffset":{"type":"integer","description":"Buffer in milliseconds subtracted from the last successful run timestamp, creating an\noverlapping window that catches records still propagating when the previous run executed.\nSet it when records created or modified near the run time are occasionally skipped due to\nreplication or indexing delays. Keep it as low as possible — larger values reprocess\nredundant records. A negative value shifts the window forward (look-ahead) instead of back."},"startDate":{"type":["string","null"],"format":"date-time","description":"Lower-bound cutoff for the first run. Omitting it doesn't export history: the first run\nthen starts at its own timestamp, an empty window. Set it to the earliest change the first\nrun should pick up. Later runs start from the flow's checkpoint, the end of the last run\nthat exported records."}}},"Test":{"type":"object","description":"Configures test exports that cap the number of records retrieved, for safely developing and\nvalidating against small data samples. Required when the export's type is \"test\"; omit for\nother export types. A test export behaves like a standard export in every other way (filters,\npagination, processing) and stores no state between runs.","properties":{"limit":{"type":"integer","default":1,"description":"Caps the total records a test run processes, counted on top-level records before\noneToMany processing; the maximum exists to prevent accidentally processing large\ndatasets during development. The cap applies across pages — processing stops once the\nlimit is reached regardless of pageSize. When transitioning to production, leave this in\nplace and change the export's type field; the limit then no longer applies.","minimum":1,"maximum":100}}},"Once":{"type":"object","description":"Configures flag-based exports that skip records an earlier run already processed. Required\nwhen the export's type is \"once\"; omit for other export types. Each run retrieves records\nwhere the tracking boolean field is false, then sets that field to true after successful\nprocessing so later runs skip them; if a run fails, the flags are left unchanged and the\nrecords are retried automatically on the next run.","properties":{"booleanField":{"type":"string","description":"API field name of the boolean/checkbox in the source system that tracks processed\nrecords: the export selects only records where this field is false, then sets it to true\nin batches after each successfully processed page. The field must be writeable by the\nexport's connection. Ensure no other process updates the same field — use a separate\nflag per export process — or records may be skipped unexpectedly."}}},"Webhook":{"type":"object","description":"Configuration for webhook listeners that receive data through incoming HTTP requests. Required when the export's `type` is `webhook`; omit for other export types. Senders POST to the export's listener URL, `https://api.integrator.io/v1/exports/{_id}/data` (or the same path on the account's regional API host); the platform validates each request using the configured `verify` method, passes the payload to subsequent flow steps, and returns a configurable HTTP response to the caller. A request is answered 204 by default when accepted, 401 when it fails verification, and 403 `unable_to_run_flows` while every flow the export feeds is disabled; a refused request is not queued, so the sender must resend it.","properties":{"provider":{"type":"string","description":"Source application sending the webhook, used to pre-configure security settings and payload parsing for that platform. Use `custom` (the default when omitted) for unlisted providers or when you need full manual control over the security configuration. Provider-specific choices may require credentials (tokens, keys) mandated by that platform.","enum":["github","shopify","travis","travis-org","slack","dropbox","onfleet","helpscout","errorception","box","stripe","aha","jira","pagerduty","postmark","mailchimp","intercom","activecampaign","segment","recurly","shipwire","surveymonkey","parseur","mailparser-io","hubspot","integrator-extension","custom","sapariba","happyreturns","typeform"]},"verify":{"type":"string","description":"Verification method applied to every incoming request before processing; required for all webhook exports. Each method needs companion fields: `hmac` requires `key`, `algorithm`, `encoding`, and `header` (except on connector-backed webhooks, where the connector definition supplies them); `token` requires `token`, with `tokenLocation` defaulting to `body` and selecting which location-specific field applies; `basic` requires `username` and `password`; `secret_url` requires `token`. Prefer `hmac` when the source system supports it — it is the most secure option; `secret_url` relies only on URL obscurity and suits non-sensitive data or testing.","enum":["token","hmac","basic","secret_url"]},"token":{"type":"string","description":"Shared secret used when `verify` is `token` or `secret_url`. For `token` verification, the value found at `path` in each request must exactly match (case- and whitespace-sensitive) or the request is rejected with a 401. For `secret_url`, the token is embedded in the webhook URL to create a hard-to-guess endpoint — generate a random, high-entropy value and treat it as a sensitive credential."},"algorithm":{"type":"string","description":"Hashing algorithm used to validate HMAC signatures when `verify` is `hmac`. Must match the algorithm the webhook sender uses — a mismatch causes every request to be rejected. Use `sha256` unless the provider explicitly requires another value.","enum":["sha1","sha256","sha384","sha512"]},"encoding":{"type":"string","description":"Encoding of the HMAC signature value when `verify` is `hmac`. Must match the encoding the webhook sender uses — a mismatch causes requests to be rejected with a 401 even when the signature is otherwise correct. With `provider: custom`, `hex` is compared as lowercase hex: an uppercase hex signature is rejected.","enum":["hex","base64"]},"key":{"type":"string","description":"Secret used to validate signatures when `verify` is `hmac`. The system computes a signature of the request body using this key and the configured `algorithm`, then compares it with the signature sent in the request header named by `header`. Treat it as a highly sensitive credential — never expose it in repositories or logs."},"header":{"type":"string","description":"Name of the request header that carries the HMAC signature when `verify` is `hmac`. Must match the header name the webhook sender uses (header names are case-insensitive); requests without this header are rejected with a 401. With `provider: custom` the header value must be the bare digest in the configured `encoding`: a value with a `sha256=` prefix is rejected with a 401."},"tokenLocation":{"type":"string","enum":["body","header","queryParam"],"default":"body","description":"Where the verification token is found when `verify` is `token`. Each location uses a different companion field: `body` uses `path`, `header` uses `tokenHeaderName` (and `tokenHeaderScheme`), `queryParam` uses `tokenQueryParamName`."},"path":{"type":"string","description":"JSON path into the request body holding the verification token when `verify` is `token` and `tokenLocation` is `body` (e.g. `meta.token`). The value at this path must exactly match `token` or the request is rejected."},"tokenHeaderName":{"type":"string","description":"Request header carrying the verification token when `verify` is `token` and `tokenLocation` is `header`."},"tokenHeaderScheme":{"type":"string","enum":["bearer","custom","none"],"default":"bearer","description":"Scheme prefixing the token in the header when `tokenLocation` is `header`."},"customTokenScheme":{"type":"string","description":"Custom header scheme prefix used when `tokenHeaderScheme` is `custom`."},"tokenQueryParamName":{"type":"string","description":"Query parameter carrying the verification token when `verify` is `token` and `tokenLocation` is `queryParam`."},"_httpConnectorId":{"type":"string","format":"objectId","description":"HTTP connector backing this listener when the webhook is provided by an assistant/connector (e.g. Slack); set by the platform for connector-backed listeners."},"requestMediaType":{"type":"string","enum":["json","xml","csv","urlencoded","plaintext"],"description":"Overrides how the incoming request body is parsed when the provider sends a non-standard content type. When omitted, the body is parsed by its Content-Type header."},"pathToRecords":{"type":"string","description":"JSON path into an object payload to the array whose elements become the records. When omitted, a top-level JSON array is emitted as one record per element and an object payload is emitted as a single record."},"includeParentData":{"type":"boolean","description":"When true and `pathToRecords` is set, each emitted record also carries the surrounding parent fields from the payload."},"username":{"type":"string","description":"Username half of the credentials when `verify` is `basic`. Incoming requests must include an `Authorization: Basic` header carrying the base64-encoded `username:password` pair. Use only over HTTPS to prevent credential interception."},"password":{"type":"string","description":"Password half of the credentials when `verify` is `basic`, validated together with `username` from the request's `Authorization` header. Use a strong, unique value and treat it as a sensitive credential."},"successStatusCode":{"type":"integer","description":"HTTP status code returned to the caller after successful processing; must be a valid 2xx code. The default 204 returns no response body and causes `successBody` to be ignored — set 200 or 202 when the caller needs a response body or expects a specific code.","default":204},"successBody":{"type":"string","description":"Response body returned to the caller after successful processing; ignored when `successStatusCode` is 204. Content type is set by `successMediaType`, and the value can be static text or structured JSON/XML, including handlebars expressions for dynamic values."},"successMediaType":{"type":"string","description":"Sets the Content-Type header on successful webhook responses. Only takes effect when a `successBody` is returned (a status code other than 204), and must match the actual format of that body.","default":"json","enum":["json","xml","plaintext"]},"successResponseHeaders":{"type":"array","description":"Custom headers added to successful webhook responses — for example CORS headers or correlation IDs. Headers defined here take precedence over automatically set headers such as Content-Type, and values support handlebars expressions for dynamic content.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the header to set on successful webhook responses; headers defined here take precedence over automatically set headers such as Content-Type."},"value":{"type":"string","description":"Value sent for the header; supports handlebars expressions for dynamic content."}}}},"challengeResponseHeaders":{"type":"array","description":"Custom headers returned for webhook subscription verification (challenge) requests, which providers send before delivering real events. Required header values vary by provider — consult the provider's documentation, since incorrect challenge headers can prevent the subscription from being verified.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the header to set on challenge (subscription verification) responses."},"value":{"type":"string","description":"Value sent for the header; consult the provider's documentation for required challenge header values."}}}},"challengeSuccessBody":{"type":"string","description":"Response body returned for webhook subscription verification (challenge) requests. Many providers require echoing back a challenge value from the request, which handlebars expressions can access — for example `{{hub.challenge}}` (Facebook/Instagram) or `{\"challenge\":\"{{challenge}}\"}` (Slack). An incorrect challenge response prevents the subscription from being verified."},"challengeSuccessStatusCode":{"type":"integer","description":"HTTP status code returned for webhook subscription verification (challenge) requests. Most providers expect the default 200; change it only when the provider's verification explicitly requires a different code.","default":200},"challengeSuccessMediaType":{"type":"string","description":"Sets the Content-Type header on challenge responses. Must match both the format of `challengeSuccessBody` and the provider's requirements — for example, Slack expects `json` while Facebook/Instagram verification expects `plaintext`.","default":"json","enum":["json","xml","plaintext"]}},"if":{"properties":{"verify":{"const":"hmac"}},"required":["verify"],"not":{"required":["_httpConnectorId"]}},"then":{"required":["key","algorithm","encoding","header"]},"else":{"if":{"properties":{"verify":{"const":"token"}},"required":["verify"]},"then":{"required":["token"],"if":{"properties":{"tokenLocation":{"const":"body"}},"required":["tokenLocation"]},"then":{"required":["path"]},"else":{"if":{"properties":{"tokenLocation":{"const":"header"}},"required":["tokenLocation"]},"then":{"required":["tokenHeaderName"]},"else":{"if":{"properties":{"tokenLocation":{"const":"queryParam"}},"required":["tokenLocation"]},"then":{"required":["tokenQueryParamName"]},"else":{"required":["path"]}}}},"else":{"if":{"properties":{"verify":{"const":"basic"}},"required":["verify"]},"then":{"required":["username","password"]},"else":{"if":{"properties":{"verify":{"const":"secret_url"}},"required":["verify"]},"then":{"required":["token"]}}}}},"Simple":{"type":"object","description":"Configuration for `simple` (data-loader) exports — exports that accept manually uploaded\nfiles through the data-loader UI instead of fetching data from a connection. Relevant only\nwhen the export's `type` is `simple`; the uploaded content is parsed using the same file\nsettings as file-based exports.","properties":{"file":{"$ref":"#/components/schemas/File"}}},"File":{"type":"object","description":"Controls how files are parsed, filtered, and processed across all file-based exports\n(FTP/SFTP, Amazon S3, simple file uploads, and other file sources). The type field selects\nthe file format and determines which format-specific object (csv, json, xlsx, xml, or\nfileDefinition) must be configured; the output field selects whether files are parsed into\nrecords, transferred as blobs, or listed as metadata only. The filter object selectively\nskips files before processing.","properties":{"encoding":{"type":"string","description":"Character encoding used to read and parse file content. If the encoding is unknown, try\nutf8 first (the default), then win1252 for Western-language files with garbled\ncharacters; consider the geographic origin of the data when selecting.","enum":["utf8","win1252","utf-16le","gb18030","macroman","iso88591","shiftjis"]},"type":{"type":"string","description":"Format of the files being processed; determines which format-specific configuration\nobject (csv, json, xlsx, xml, or fileDefinition) must be populated — other format\nobjects are ignored. Required for all file-based exports except blob exports (export\ntype \"blob\" or output \"blobKeys\").","enum":["csv","json","xlsx","xml","filedefinition"]},"output":{"type":"string","description":"Processing mode for retrieved files: parse contents into records, transfer files as\nunparsed blobs, or return only file metadata. Determines what data is passed to\nsubsequent flow steps.","enum":["records","metadata","blobKeys"]},"skipDelete":{"type":"boolean","default":false,"description":"When true, source files remain on the file server after processing; when false (the\ndefault), files are deleted after successful processing. Files that fail processing are\nnever deleted, regardless of this setting. Enable retention when files must be processed\nby other flows or systems, or kept for compliance."},"compressionFormat":{"type":"string","description":"Compression format of incoming files, which are decompressed before any other processing\n(parsing, filtering). Set this only when the source always delivers compressed files —\nif a file marked as compressed is not actually compressed, processing fails. Leave unset\nwhen files arrive uncompressed or only sometimes compressed.","enum":["gzip","zip"]},"purgeInternalBackup":{"type":"boolean","description":"When true, Celigo keeps no internal backup copies of files processed by this export;\nwhen false (the default), copies are retained for your account's retention period and\nare available for reprocessing or troubleshooting. Applies only to this export and only\nto Celigo's internal copies — source files are governed by skipDelete. Enable for highly\nsensitive data or zero-retention policies; without backups, recovery may require\nre-obtaining files from the source system."},"decrypt":{"type":"string","description":"Decryption applied to incoming files before any other processing; decryption runs before\ndecompression, and a decryption failure fails the file's processing entirely. The\nconnection must already be configured with the private key (and passphrase, if\napplicable) matching the public key used to encrypt the files. Only PGP/GPG encryption\nis currently supported.","enum":["pgp"]},"batchSize":{"type":["integer","null"],"description":"Number of files retrieved per batch; if a batch fails, the whole batch is retried\n(stored as `null` when not configured). Use\nlower values (10-50) for large files to reduce timeout and memory pressure, and higher\nvalues for many small files to improve throughput. Controls file retrieval only — record\npaging is governed by the export's pageSize.","maximum":1000},"sortByFields":{"type":"array","description":"Sorts the records parsed from each file before they are processed, establishing a\ndeterministic processing order (for example, chronological or priority-based). Sorting\nhappens after parsing but before any filtering or grouping, and does not modify the\nsource files. Sorting by the same fields used in groupByFields improves grouping\nperformance.","items":{"type":"object","properties":{"field":{"type":"string","description":"Record field to sort by; use dot notation for nested properties (e.g.\ncustomer.name). Field names are case-sensitive."},"descending":{"type":"boolean","description":"When true, sorts this field in descending order (newest/highest first); when false\nor omitted, sorts ascending. Directions can be mixed across fields in a\nmulti-field sort."}}}},"groupByFields":{"$ref":"#/components/schemas/GroupBy"},"groupEmptyValues":{"type":"boolean","description":"When true, records whose groupByFields values are empty are still grouped together rather\nthan each forming its own group; when false (the default), empty-keyed records are not\ngrouped. Only relevant when groupByFields is set."},"csv":{"type":"object","description":"Parsing settings for delimiter-separated text files (CSV, TSV, pipe-delimited, and\nsimilar). Configure when type is \"csv\".\n\nRequired when type is csv.","properties":{"columnDelimiter":{"type":"string","description":"Character sequence separating fields within each row; comma when omitted. Use \"\\t\"\nfor tab-delimited files; European-locale exports often use semicolons. A wrong\ndelimiter doesn't fail the parse: with a header row, each row becomes one field named\nafter the whole header line (`{\"order_id;sku;qty\": \"1001;TEE-M;2\"}`), so the failure\nshows up in later steps."},"rowDelimiter":{"type":"string","description":"Character sequence marking the end of each record; auto-detected from file content\nwhen omitted. Set explicitly (\"\\n\", \"\\r\\n\", or \"\\r\") only when auto-detection fails,\nsuch as for files with mixed line endings — an incorrect value merges or splits\nrecords."},"hasHeaderRow":{"type":"boolean","description":"When true (the default), the first row is read as field names rather than data, and\nthose names are available in mappings. When false, every row is treated as a data\nrecord and fields are referenced by position."},"trimSpaces":{"type":"boolean","description":"When true, removes leading and trailing whitespace from every field value during\nparsing; when false (the default), whitespace is preserved exactly as in the source.\nHeader row values are always trimmed regardless of this setting, and spaces between\nwords are never affected."},"rowsToSkip":{"type":"integer","description":"Number of rows at the top of the file to ignore before parsing begins — useful for\nreport titles, timestamps, or other metadata above the data. The header row (when\nhasHeaderRow is true) is expected after the skipped rows."},"disableQuoteAndStripEnclosingQuotes":{"type":"boolean","description":"When true, disables CSV quote processing: quotes are treated as literal characters,\nenclosing quotes are stripped, and delimiters inside quoted text split the field.\nWhen false (the default), standard RFC 4180 quoting applies and quoted fields\nprotect embedded delimiters. Enable only for files with non-standard or malformed\nquoting; review this setting first when field counts vary unexpectedly between rows."},"keyColumns":{"type":"array","description":"Field names whose values together identify a record's group when consecutive rows\nshare the same key — used to merge multi-line records (e.g. an order header repeated\nacross line-item rows) into a single record. Leave empty for one record per row; the\ncolumns must exist in the parsed header.","items":{"type":"string"}}}},"json":{"type":"object","description":"Parsing settings for JSON files. Configure when type is \"json\". resourcePath locates the\narray of records when they are nested inside a container object; malformed JSON fails\nthe entire file's processing.\n\nRequired when type is json.","properties":{"resourcePath":{"type":"string","description":"Dot-notation path to the array of records within the JSON structure (e.g.\n\"response.data.customers\"); leave empty when the file's root is already the record\narray. The path must resolve to an array of objects — array indexing and wildcards\nare not supported. A path that resolves to nothing produces zero records without\nraising an error."}}},"xlsx":{"type":"object","description":"Parsing settings for Microsoft Excel (.xlsx) workbooks. Configure when type is \"xlsx\".\nCalculated cell values are extracted rather than formulas; legacy .xls files are not\nsupported — use the modern Open XML format.\n\nRequired when type is xlsx.","properties":{"hasHeaderRow":{"type":"boolean","description":"When true (the default), the first row is read as field names rather than data;\nblank header cells are auto-named and duplicate names are made unique with suffixes.\nWhen false, every row is treated as data and fields receive generic positional names\n(Column1, Column2, ...)."}}},"xml":{"type":"object","description":"Parsing settings for XML documents. Configure when type is \"xml\". resourcePath is an\nXPath expression selecting the elements treated as records; namespaces are handled\nautomatically.\n\nRequired when type is xml.","properties":{"resourcePath":{"type":"string","description":"XPath expression selecting the elements treated as records — each matching element\nbecomes one record, with its child elements as fields. Required for XML parsing;\nthere is no default. Use an absolute path (/Root/Order) when the structure is fixed,\n//Element to match at any depth, or predicates (//Element[@type=\"product\"]) to\nfilter; XPath is case-sensitive, and a non-matching path yields zero records."}}},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"},"fileDefinition":{"type":"object","description":"Parsing via a predefined file definition resource, for formats the standard parsers\ncannot handle — fixed-width files, EDI documents (X12, EDIFACT), and multi-record-type\nor proprietary formats. Configure when type is \"filedefinition\".\n\nRequired when type is filedefinition.","properties":{"_fileDefinitionId":{"type":"string","format":"objectId","description":"File definition resource containing the parsing rules for this format. Must\nreference an existing, accessible file definition — never guess or fabricate the ID.\nDefinitions are reusable across exports, so changing one affects every export that\nuses it."},"allowPartialSuccess":{"type":"boolean","description":"When true, a file whose records partially fail parsing against the file definition\nstill emits the records that did parse, instead of failing the entire file. Applies to\nfile-definition (fixed-width, EDI) parsing only. For X12 and EDIFACT the unit is the\ntransaction set (ST–SE, UNH–UNT), and it takes effect only with an EDI profile linked:\na failed set is set aside and reported as an error while the file's other sets go on\nas records. Without this flag, one failed set fails the whole file."}}},"filter":{"description":"Selects which files are processed: files matching the filter criteria are exported,\nand non-matching files are skipped entirely before processing begins. Filterable\nfields come from each file's listing entry: the name as `fileName`, the size as\n`filesize`, and the modified time as `lastModifiedTime` on FTP, SFTP and S3 or\n`modifiedTime` on cloud-storage folders (`http.type: \"file\"`)\n(e.g. [\"endswith\", [\"string\", [\"extract\", \"fileName\"]], \".csv\"]). There is no\n`lastmodified` key.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"backupPath":{"type":"string","description":"Path where backup copies of source files are stored before processing."},"includeSubdirectories":{"type":"boolean","default":false,"description":"When true, an Azure Data Lake Storage Gen2 transfer (`assistant: azuredatalakestoragegen2`,\n`http.type: file`) also picks up files in the subdirectories below the directory path in\n`http.relativeURI`; when false or omitted only that directory's own files are transferred."},"directory":{"type":"object","required":["pathMode"],"description":"Structured source location for cloud file-provider sources (Google Drive shared drives,\nBox, Dropbox, Google Cloud Storage, Azure Data Lake Storage Gen2), replacing the flat path\nused by classic FTP/S3 sources. Select the location by folder ID or by a path relative to\na configured storage root via pathMode. For the drive-style providers, directoryId mode\naddresses the folder by id alone — the flat relative-path field is not consulted at\nruntime. Google Cloud Storage puts the bucket in `storageRootId` and the object prefix in\n`id` with `pathMode: directoryId`; Azure Data Lake Storage Gen2 puts the container in\n`name` and the directory path in `http.relativeURI`.","properties":{"pathMode":{"type":"string","enum":["relativePath","directoryId"],"description":"How the directory is addressed — by provider folder ID, or by a path relative to a configured storage root."},"id":{"type":"string","description":"Provider-native folder ID of the source directory (e.g. a Google Drive folder ID); set\nwhen pathMode is directoryId. This is the file provider's own ID, not a Celigo resource ID.\nFor Google Cloud Storage it is the object prefix inside the bucket, ending with `/`."},"name":{"type":"string","description":"Display name of the folder identified by id, kept for readability in the UI; the\nfolder browser fills it when the location is picked visually. For Azure Data Lake\nStorage Gen2 it is the authoritative container name instead — the form saves the\ncontainer here and no id."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root (e.g. a Google Drive shared-drive ID) the relative\npath resolves against; set when pathMode is relativePath. The provider's own ID, not a Celigo ID.\nFor Google Cloud Storage it is the bucket name."},"storageRootName":{"type":"string","description":"Display name of the storage root identified by storageRootId, retained for reference in the UI."}}},"backupDirectory":{"type":"object","required":["pathMode"],"description":"Structured destination for backup copies of source files on cloud file providers — the\nfile-provider counterpart to backupPath. Addressed the same way as directory.","properties":{"pathMode":{"type":"string","enum":["relativePath","directoryId"],"description":"How the backup location is addressed — by provider folder ID, or by a path relative to a configured storage root."},"id":{"type":"string","description":"Provider-native folder ID of the backup directory; set when pathMode is directoryId."},"name":{"type":"string","description":"Display name of the backup folder identified by id, kept for readability in the UI;\nthe folder browser fills it when the location is picked visually."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root the backup relative path resolves against; set when pathMode is relativePath."}}}}},"IncludeParentData":{"type":"boolean","description":"When true and a path to records is configured on the export (`pathToRecords` for MongoDB and DynamoDB, `http.response.resourcePath` for HTTP, `file.json`/`file.xml` `resourcePath` for file-based exports), the data outside that path is attached to each extracted record as a `_PARENT` object, keeping the surrounding envelope available to downstream steps. When omitted or false, records are emitted without `_PARENT`, exactly as before. Constraints: on HTTP exports this option cannot be combined with `useXpathParser: true` (rejected at save with `EXPORT_INCLUDE_PARENT_DATA_XPATH_UNSUPPORTED`), and XML responses must resolve to the default converted-JSON parse mode — a run whose XML parser resolves to DOM/XPath mode (for example, because a connection-level field uses an advanced XPath expression) fails at start with the same error rather than silently omitting `_PARENT`. On file-based exports, it requires `file.type` of `json` with `file.json.resourcePath` configured — XML file envelope capture is not yet supported (the streaming XML parser emits no envelope event; XML support arrives with a parsers-package enhancement) — and any other file configuration is rejected at save with `EXPORT_INCLUDE_PARENT_DATA_UNSUPPORTED_FILE_TYPE`."},"Filter":{"type":"object","description":"Configuration for selectively processing records based on specified criteria. This object enables\nprecise control over which items are included or excluded from processing operations.\n\n**Filter behavior**\n\nWhen configured, the filter is applied before processing begins:\n- Items that match the filter criteria are processed\n- Items that don't match are completely skipped\n- No partial processing is performed\n\nSend the whole object. A rules array sent alone as the `filter` value is not\nstored, and a full update of an export that carries one removes the filter the\nexport had, with no error.\n\n**Implementation approaches**\n\nThere are two distinct filtering mechanisms available:\n\n**Rule-Based Filtering (`type: \"expression\"`)**\n- **Best For**: Common filtering patterns based on standard attributes\n- **Capabilities**: Filter by names, values, dates, numerical ranges, text patterns\n- **Advantages**: Declarative, no coding required, consistent performance\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear, static criteria for selection\n\n**Script-Based Filtering (`type: \"script\"`)**\n- **Best For**: Complex logic, dynamic criteria, or business rules\n- **Capabilities**: Full programmatic control, access to complete metadata\n- **Advantages**: Maximum flexibility, can implement any filtering logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Simple rules aren't sufficient or logic needs to be dynamic\n","properties":{"type":{"type":"string","description":"Determines which filtering mechanism to use. This choice affects which properties\nmust be configured and how filtering logic is implemented.\n\n**Available types**\n\n**Rule-Based Filtering (`\"expression\"`)**\n- **Required Config**: The `expression` object with rule definitions\n- **Behavior**: Evaluates declarative rules against item attributes\n- **Best For**: Common patterns like name matching, date ranges, value limits\n- **Advantages**: Simpler to configure, no custom code required\n\n**Script-Based Filtering (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to determine which items to process\n- **Best For**: Complex conditions, business logic, dynamic criteria\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard filtering needs (name, size, date), use `\"expression\"`\n2. For complex logic or conditions not covered by expressions, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based filtering. This object enables filtering\nitems based on common attributes without requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define filtering rules that\ncan match against item attributes like name, type, value, date, and other properties.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Rules can be combined with AND/OR logic\n- Each rule can check a specific attribute\n- Multiple conditions can be applied (ranges, pattern matching, exact matches)\n\n**Common filter patterns**\n\n1. **Pattern matching**: Regular expressions with `matches`\n2. **Value range filtering**: Numbers between min and max values\n3. **Date range filtering**: Items created/modified within specific time ranges\n4. **Status checking**: Items with specific status values or properties\n\nFor AI agents: Rule-based filtering should be your first choice when the filtering criteria\ncan be expressed in terms of standard attributes. Only use script-based filtering when\nmore complex logic is required.\n","properties":{"version":{"type":"string","description":"Version identifier for the expression format. Currently only version \"1\" is supported.\n\nThis field ensures future compatibility if the expression format evolves. Always set to \"1\"\nfor current implementations.\n","enum":["1"]},"rules":{"type":"array","description":"Expression array defining filter conditions using prefix notation. The first element is the operator,\nfollowed by its operands which may themselves be nested expression arrays.\n\nThe rule expression follows this pattern:\n- First element: Operator name (string)\n- Remaining elements: Operands for that operator (values or nested expressions)\n\n**Expression structure**\n\nFilter expressions use a prefix notation where operators appear before their operands:\n```\n[operator, operand1, operand2, ...]\n```\n\n**Comparison Operators**\n- `\"equals\"`: Exact match (equals)\n- `\"notequals\"`: Not equal to value (not equals)\n- `\"greaterthan\"`: Value is greater than specified value (is greater than)\n- `\"greaterthanequals\"`: Value is greater than or equal to specified value (is greater than or equals)\n- `\"lessthan\"`: Value is less than specified value (is less than)\n- `\"lessthanequals\"`: Value is less than or equal to specified value (is less than or equals)\n- `\"startswith\"`: String starts with specified prefix (starts with)\n- `\"notstartswith\"`: String does not start with specified prefix (does not start with)\n- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"notendswith\"`: String does not end with specified suffix (does not end with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"empty\"`: Field is missing or empty (is empty)\n- `\"notempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches a regular expression, ignoring case (matches)\n\nOperator names are case-insensitive: `NOTEMPTY` and `endsWith` work and are stored\nas sent. `isempty`, `isnotempty`, `anyof`, `notcontains`, `doesnotstartwith`,\n`doesnotendwith`, `notmatches`, `doesnotmatch` and `notequal` are not operators:\nthey are refused with 422 \"Invalid operation: <name>\", with the name in lowercase,\nboth when the filter is saved and by `POST /v1/processors/filter`. To negate a\ncondition, wrap it in `not`.\n\nThe string comparisons (`equals`, `contains`, `startswith`, `endswith` and their\nnegations) are case-sensitive; wrap the field in `lowercase` to compare without case.\n`matches` is not: every pattern is compiled with the flags `gi`, so `^paid$` also\nmatches `PAID` and `[a-z]` matches capital letters. An inline flag such as `(?i)`\nmakes the pattern invalid. The filter still saves, but in a run every record fails\nwith `FILTER_EVALUATION_FAILED` (\"Invalid regular expression: /(?i)…/gi: Invalid\ngroup\"), and `POST /v1/processors/filter` refuses the call with 422 (live-verified\n2026-10-22).\n\n**Logical Operators**\n- `\"and\"`: All conditions must be true\n- `\"or\"`: At least one condition must be true\n- `\"not\"`: Negates the condition\n\n**Field Access and Type Conversion**\n- `\"extract\"`: Access a field from the item by name\n- `\"settings\"`: Access a custom setting from the flow, flow step, or integration configuration\n- `\"boolean\"`: Convert value to Boolean type\n- `\"epochtime\"`: Convert value to Epoch Time (Unix timestamp)\n- `\"number\"`: Convert value to Number type\n- `\"string\"`: Convert value to String type\n\n**Field Access Details**\n\n**Using `extract` to access record fields:**\n- Retrieves values from the current record being processed\n- Can access nested properties using dot notation (e.g., `\"customer.email\"`)\n- Paths are relative to the record itself: `[\"extract\", \"record.status\"]`\n  matches nothing\n- In a record filter, must be wrapped in a type conversion (`string`, `number`,\n  `boolean` or `epochtime`): a bare `[\"extract\", ...]` operand is refused with 422\n  \"Field operation extract must be called by a Type Conversion Operation but was called\n  by <operator>\", both when an import's filter is saved and by\n  `POST /v1/processors/filter`\n\n**Using `settings` to access configuration values:**\n- Retrieves values from the integration's configuration settings\n- Supports different scopes with prefix notation:\n  - `flow.settingName`: Access flow-level settings\n  - `flowGrouping.settingName`: Access the flow group's settings\n  - `export.settingName`: Access the export's (or lookup's) own settings\n  - `import.settingName`: Access the import's own settings\n  - `integration.settingName`: Access integration-level settings\n  - `connection.settingName`, `iClient.settingName`: Access the step's\n    connection and iClient settings\n- A scope whose resource has no settings can be absent; a rule that reads\n  it matches nothing\n- Must be wrapped in a type conversion like `extract`: a bare\n  `[\"settings\", ...]` operand is refused with 422 \"Field operation settings\n  must be called by a Type Conversion Operation\"\n- Useful for dynamic filtering based on configuration\n\n**Field Transformations**\n- `\"lowercase\"`: Convert string to lowercase\n- `\"uppercase\"`: Convert string to uppercase\n- `\"ceiling\"`: Round number up to the nearest integer\n- `\"floor\"`: Round number down to the nearest integer\n- `\"abs\"`: Get absolute value of a number\n\n**Arithmetic** (number operands)\n- `\"add\"`, `\"subtract\"`, `\"multiply\"`, `\"divide\"`, `\"modulo\"`: for example\n  `[\"subtract\", [\"number\", [\"extract\", \"total\"]], [\"number\", [\"extract\", \"discount\"]]]`.\n  `mod` is not an operator (422 \"Invalid operation: mod.\").\n\n**Dates**\n- `epochtime` reads ISO 8601 strings and epoch-millisecond numbers. An empty\n  string, a string that isn't a date, or epoch milliseconds sent as a string\n  fail it with \"Invalid date. Cannot convert given string or number to date\n  time.\", followed by `; record=` and the record as JSON: in a run that record fails (`FILTER_EVALUATION_FAILED`, source\n  `output_filter` on an export), and `POST /v1/processors/filter` and an\n  export preview refuse the whole call with 422. `and` and `or` evaluate\n  every operand, so a `notempty` test beside it doesn't prevent the failure.\n  A missing or null field doesn't match.\n- `datetime`, `date` and `timestamp` are not type conversions: 422\n  \"Invalid operation: <name>.\"\n- `number` reads a missing or empty field as 0.\n\n**Handlebars operands**\n- An operand can also be a string holding exactly one Handlebars expression, such as\n  `\"{{record.status}}\"` or `\"{{lowercase record.code}}\"`, on either side of the\n  operator. It renders against the record (`record.<field>`; an unprefixed\n  `{{status}}` renders empty) before the comparison.\n- The rendered value is a string: wrap it in `[\"number\", ...]` to compare it with a\n  number, or the rule is refused with 422.\n- `epochtime` does not render Handlebars: `[\"epochtime\", \"{{timestamp}}\"]` is refused\n  with 422 \"invalid datetime format\".\n- A string with text around the expression, or with more than one expression, is\n  compared as a literal.\n- `[\"number\", \"{{timestamp \\\"x\\\"}}\"]` is the current time in epoch milliseconds,\n  so `[\"greaterthanequals\", [\"epochtime\", [\"extract\", \"created\"]], [\"subtract\",\n  [\"number\", \"{{timestamp \\\"x\\\"}}\"], 604800000]]` keeps records created in the\n  last 7 days.\n- An unknown helper fails the rule with 422 \"Failed to evaluate handlebars\n  expression\".\n\nChoose the type conversion that matches the comparison — `number` for numeric\ncomparisons, `epochtime` for dates, `string` for text. For example:\n\n```json\n[\n  \"equals\",\n  [\n    \"number\",  // Convert to number before comparison\n    [\n      \"extract\",\n      \"quantity\"\n    ]\n  ],\n  100\n]\n```\n\nExample with datetime conversion:\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",  // Convert to Unix timestamp before comparison\n    [\n      \"extract\",\n      \"createdDate\"\n    ]\n  ],\n  1609459200000  // January 1, 2021 as Unix timestamp in milliseconds\n]\n```\n\nExample with transformations:\n```json\n[\n  \"and\",\n  [\n    \"matches\",\n    [\n      \"lowercase\",  // Convert to lowercase before matching\n      [\n        \"string\",\n        [\n          \"extract\",\n          \"categories\"\n        ]\n      ]\n    ],\n    \"netsuite\"\n  ],\n  [\n    \"notequals\",\n    [\n      \"string\",\n      [\n        \"extract\",\n        \"recurrence.pattern.type\"\n      ]\n    ],\n    \"\"\n  ]\n]\n```\n\nExample comparing a record field with a flow setting:\n```json\n[\n  \"equals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"trantype\"\n    ]\n  ],\n  [\n    \"string\",\n    [\n      \"settings\",\n      \"flow.trantype\"\n    ]\n  ]\n]\n```\n\n**Examples**\n\nExample 1: Status field is not equal to \"cancelled\"\n```json\n[\n  \"notequals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"status\"\n    ]\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"filename\"\n    ]\n  ],\n  \"HC\"\n]\n```\n\nExample 3: Amount is greater than 100\n```json\n[\n  \"greaterthan\",\n  [\n    \"number\",\n    [\n      \"extract\",\n      \"amount\"\n    ]\n  ],\n  100\n]\n```\n\nExample 4: Order date is after January 1, 2023 (UTC). Compare dates as `epochtime`\nagainst epoch milliseconds: a `string` comparison of ISO timestamps orders values with\ndifferent UTC offsets wrongly.\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",\n    [\n      \"extract\",\n      \"orderDate\"\n    ]\n  ],\n  1672531200000\n]\n```\n","items":{"oneOf":[{"title":"String","type":"string"},{"title":"Number","type":"number"},{"title":"Boolean","type":"boolean"},{"title":"Object","type":"object"},{"title":"Array","type":"array"},{"title":"Null","type":"null"}]}}}},"script":{"type":"object","description":"Configuration for programmable script-based filtering. This object enables complex, custom\nfiltering logic beyond what expression-based filtering can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to determine which items\nshould be processed.\n\n**Implementation approach**\n\nScript-based filtering works by:\n1. Executing the specified function from the referenced script\n2. Passing item data to the function\n3. Using the function's return value (true/false) to determine inclusion\n\n**Common use cases**\n\nScript filtering is ideal for:\n- Complex business logic that can't be expressed as simple rules\n- Dynamic filtering criteria that change based on external factors\n- Content-based filtering that requires deep inspection\n- Advanced pattern matching beyond simple wildcards\n- Multi-stage filtering with intermediate logic\n\nFor AI agents: Only use script-based filtering when expression-based filtering is insufficient.\nScript filtering requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to the Script resource that contains the filtering logic. This must be a valid\nObjectId of a Script resource in this account: any other id fails the save with 422\n(\"Only a script owned by the user or a script shared by the Integration App can be used\nat filter.script._scriptId.\").\n\nThe referenced script must contain the function specified in the `function` field\nand must be written to handle filtering specifically. The script receives\nitem data as its input and must return a boolean value indicating whether\nto process the item (true) or skip it (false).\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n"},"function":{"type":"string","description":"Name of the function within the script to execute for filtering decisions. This function\nmust exist in the script referenced by _scriptId.\n\n**Function requirements**\n\nThe specified function must:\n- Accept item data as its first parameter\n- Return a boolean value (true to process the item, false to skip it)\n- Handle errors gracefully\n- Execute efficiently (as it may run for many items)\n\n**Function signature**\n\n```javascript\nfunction filterItems(itemData) {\n  // itemData contains properties of the item being evaluated\n  // Custom logic here\n  return true; // or false to skip the item\n}\n```\n\nFor AI agents: Ensure the function name exactly matches a function defined in the\nreferenced script, as mismatches will cause the filter to fail.\n"}}}}},"Distributed":{"type":"object","description":"Authentication settings for distributed (real-time listener) exports.","properties":{"bearerToken":{"type":"string","description":"Bearer token that a system pushing data into this export sends as\n`Authorization: Bearer <token>`. Optional — most distributed listeners (NetSuite,\nSalesforce real-time) use no token. Write-only and stored encrypted; treat it as a\nsensitive credential.","format":"password"}}},"Cdc":{"type":"object","description":"Change-data-capture configuration for real-time listener exports (the \"Listen for real-time\ndata\" source step). Present only when `type` is `stream`. Set on `MongodbExport` (MongoDB change\nstreams) and `RDBMSExport` (SQL Server CDC and PostgreSQL logical replication); the listener\nstreams change events continuously instead of fetching records on a schedule.\n\nOn save the API normalizes the `properties` grid into the typed fields: a `snapshot.mode`\nentry populates `snapshotMode` and a `capture.mode` entry populates `captureMode`. Every\nstream export must resolve a snapshot mode from one of those two places.","properties":{"captureMode":{"type":"string","enum":["change_streams_update_full","change_streams_update_full_with_pre_image"],"default":"change_streams_update_full","description":"How MongoDB change-stream events are materialized into records. Use\n`change_streams_update_full_with_pre_image` when downstream steps need the document's prior\nstate. MongoDB listeners only — `RDBMSExport` (SQL Server CDC, PostgreSQL) does not set this."},"snapshotMode":{"type":"string","enum":["initial","no_data","when_needed","initial_only"],"default":"no_data","description":"Whether the listener seeds existing rows before streaming changes. Use `initial` to backfill\nexisting data on the first run; `no_data` to stream only new changes."},"slotName":{"type":"string","pattern":"^[a-z_][a-z0-9_]{0,62}$","description":"Name of the PostgreSQL logical replication slot the listener consumes. PostgreSQL listeners\nonly. When omitted, the platform manages the slot name. Must be a valid PostgreSQL\nidentifier (lowercase letters, numbers, or underscores, starting with a letter or\nunderscore)."},"publicationName":{"type":"string","pattern":"^[a-z_][a-z0-9_]{0,62}$","description":"Name of the PostgreSQL publication that defines which tables stream changes. PostgreSQL\nlisteners only. When set, the publication is the sole table filter and `rdbms.tables`\nstays unset. Must be a valid PostgreSQL identifier."},"cursor":{"type":"string","description":"Server-managed stream position marker (where the listener resumes reading change events).\nMaintained by the platform as events are consumed; the UI exposes a reset workflow for it\nunder Cursor Management."},"paths":{"type":"array","items":{"type":"string"},"description":"Dot-notation paths within each raw change event to retain in the emitted record. MongoDB\nlisteners default to `[\"payload\"]` (the whole change document); SQL Server and PostgreSQL\nlisteners commonly select `payload.after`, `payload.op`, and related fields to expose the\nchanged row and operation type."},"intervalTime":{"type":"integer","default":300,"description":"Polling interval, in seconds, at which the listener checks the source for new change events."},"properties":{"type":"array","description":"Connector-engine settings passed through to the underlying CDC connector as name/value\npairs. Mirrors the form's additional-properties grid: the required keys (`capture.mode`\nand `snapshot.mode` for MongoDB, `snapshot.mode` for SQL Server and PostgreSQL) are\nserialized first, followed by optional keys such as `field.exclude.list`,\n`column.include.list`, or `snapshot.collection.filter.overrides.<db>.<collection>`.\nOn save the API also lifts `snapshot.mode` / `capture.mode` entries into the typed\n`snapshotMode` / `captureMode` fields.","items":{"type":"object","required":["name","value"],"properties":{"name":{"type":"string","description":"Connector property key (e.g. `capture.mode`, `snapshot.mode`, `field.exclude.list`)."},"value":{"type":"string","description":"Connector property value."}}}}}},"Topic":{"type":"object","description":"Subscription settings for an Event Streams listener (`adaptorType: TopicExport`). The\nlistener consumes messages published to the topic and hands them to the flow in batches;\nthe platform delivers them internally, so the export carries no `_connectionId`. Present\nwhen `adaptorType` is `TopicExport`; the server fills `startPosition` and `maxWaitTimeMs`\nwith their defaults when they are omitted.","properties":{"_topicId":{"type":"string","format":"objectId","description":"Topic this listener subscribes to. Create or look it up with `GET /v1/topics`; the\ntopic must belong to the same account. Pointing an existing listener at a different\ntopic starts a new subscription, read from `startPosition`. A clone made in the same\nenvironment — of this export, or of a flow that contains it — keeps `_topicId`, so the\ncopy becomes another listener on the topic once its flow is enabled."},"startPosition":{"type":"string","enum":["latest","earliest"],"default":"latest","description":"Where the listener starts reading when it first subscribes to `_topicId`. Use `earliest`\nto backfill retained history, `latest` (the default) to process only new messages. Read\non create and whenever `_topicId` changes; on other updates the stored value is kept even\nif a different value is sent."},"maxWaitTimeMs":{"type":"integer","minimum":1000,"default":30000,"description":"Longest time, in milliseconds, the listener waits to fill a batch before delivering\nwhatever it has collected. Lower values reduce latency for sparse topics; the default\nof 30 seconds favors fuller batches. The batch record cap is the export's `pageSize`."}}},"FileSystem":{"type":"object","description":"Defines which files to read from a local or mounted folder on the host running the\non-premise agent. Required when the _connectionId field references a file-system (on-premise)\nconnection; must not be included for other connection types.","required":["directoryPath"],"properties":{"directoryPath":{"type":"string","description":"Folder on the on-premise agent's host to read files from; the agent's OS account must\nhave read permission on it. Accepts a local OS path or a UNC network share, and supports\nhandlebars templates for dynamic folders."}}},"Http":{"type":"object","description":"Configuration for HTTP exports. Required when the export's `_connectionId` references a\nconnection of type `http`, and on `NetSuiteHTTPExport` (NetSuite's REST APIs on a `netsuite`\nconnection, where `nsDomainType` supplies the host).","properties":{"type":{"type":"string","enum":["file","blob","csvstream"],"description":"Set to `file` when the export works with files rather than a record-returning endpoint: raw file downloads (PDFs, images, binary data) and the folder mode of the cloud file providers (Google Drive, Box, Dropbox, Azure Blob, Google Cloud Storage, Celigo Storage), where each file in the folder is parsed by the `file` object's `type` (csv, json, xml, xlsx, filedefinition — including EDI) into records; the `file` object must also be configured. Leave undefined for standard data exports that return structured records (JSON, XML, GraphQL, SOAP responses) — the response is then parsed into records for downstream steps. When set to `file` or `blob`, the export appears as a \"Transfer\" step in Flow Builder instead of a standard \"Export\" step."},"formType":{"type":"string","enum":["http","rest","graph_ql","assistant","assistant_graphql"],"description":"Authoring mode of the HTTP request form. Controls which editor the UI presents\n(standard HTTP, legacy REST, GraphQL, or connector-assistant driven) and how the\nrequest fields are interpreted. When omitted, the standard HTTP form is used."},"method":{"type":"string","description":"HTTP method used to request data from the target API. Consult the target API's\ndocumentation to determine the appropriate method. `HEAD` exists for connector endpoints\nthat return metadata in response headers only (Azure Data Lake Storage Gen2 path\nproperties).","enum":["GET","POST","PUT","PATCH","DELETE","HEAD"]},"followRedirects":{"type":"boolean","default":true,"description":"When explicitly false, 3xx responses are not followed — the redirect\nresponse itself (status code, `Location` header, body) becomes the\nrecord. Omitted or true follows allowed redirects (the default\nbehavior)."},"maxRedirects":{"type":"integer","minimum":1,"maximum":10,"description":"Caps how many consecutive 3xx redirects are followed. Only applies\nwhen `followRedirects` is not false; to not follow at all, set\n`followRedirects: false` rather than `maxRedirects: 0`. Decimal\nvalues are rejected on save."},"relativeURI":{"type":"string","description":"Resource path appended to the connection's `baseURI` to form the complete request URL. Path segments, query parameters, or the entire value can be built with handlebars expressions for endpoints determined at runtime. For lookup exports (`isLookup: true`) with mappings configured, handlebars always render against the original pre-mapped input record, so fields removed or renamed by mappings remain available for URI construction. On `NetSuiteHTTPExport` the base URL is derived from the NetSuite connection and `nsDomainType`, so the path starts at `/services/rest/...` or `/app/site/hosting/restlet.nl`.\n\nLeave it empty to call the connection's `baseURI` exactly; `/` appends a trailing slash, which is a different URL. An export saved with an empty relative URI and no `http.body` comes back from reads without its `http` block, so a PUT built from such a read loses the stored settings: send the complete `http` block with every PUT."},"headers":{"type":"array","description":"Headers specific to this export, merged with (and able to override) headers defined on the connection. Define common headers such as authentication on the connection instead. Values support handlebars expressions; for lookup exports with mappings, values render against the pre-mapped input record.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header to send; a header defined here overrides a same-named header from the connection."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions; for lookup exports with mappings, the value renders against the pre-mapped input record."}}}},"requestMediaType":{"type":"string","description":"Overrides the connection-level request media type for this export. Set only when this endpoint requires a different format than the connection default.","enum":["json","xml","urlencoded","form-data","plaintext"]},"body":{"type":"string","description":"Request body sent with POST, PUT, or PATCH requests, typically carrying query or filter criteria for APIs (such as GraphQL or SOAP) that expect them in the body. The content must match the format set by `requestMediaType` and supports handlebars expressions for dynamic values."},"successMediaType":{"type":"string","description":"Media type used to parse successful response bodies. Set only when the response format differs from the request format.","enum":["json","xml","csv","plaintext"]},"errorMediaType":{"type":"string","description":"Media type used to parse error response bodies. Set only when error responses use a different format than the request.","enum":["json","xml","plaintext"]},"_asyncHelperId":{"type":"string","format":"objectId","description":"AsyncHelper resource that handles polling for long-running operations on APIs that process requests asynchronously (HTTP 202 responses, job tickets, feed or document IDs). Set when the export must submit a request, poll for status, and retrieve results once the external process completes — for example Amazon SP-API feeds or large report generators."},"once":{"type":"object","description":"Callback configuration for once exports, used to mark records as exported in the source system after successful processing.\nCalled only while the export's `type` is `once`: an export with no `type` keeps a saved `once` and never calls it, so every\nrun reads every record again (live-verified 2026-10-22).","properties":{"relativeURI":{"type":"string","description":"Relative path (starting with `/`) called on the source system to mark each record as exported after successful processing. Supports handlebars variables and renders against the pre-mapped record — mappings do not apply to the callback URI."},"method":{"type":"string","description":"HTTP method used for the mark-as-exported callback request.","enum":["GET","PUT","POST","PATCH","DELETE"]},"body":{"type":"string","description":"Request body sent with the mark-as-exported callback. Supports handlebars expressions for dynamic values."}}},"paging":{"type":"object","description":"Controls how the export requests subsequent pages when the API returns multi-page responses. The `method` field determines which companion fields are required and how each next page is requested. For the page, skip, and token methods, reference the matching pagination variable (`{{export.http.paging.page}}`, `{{export.http.paging.skip}}`, or `{{export.http.paging.token}}`) in the relative URI or request body — misconfigured pagination is a common cause of incomplete data retrieval.\n\nWhen this export is the first lookup of a builder-mode API with `pagination.enabled` set, it is that API's volume driver: saving the export (even unchanged) invalidates the API's issued cursors, so a caller mid-walk gets an empty final page, and removing `paging` is accepted on save but makes every call to the API fail with `400 api_pagination_source_not_paged` until paging is restored or the API's flag is turned off.\n\nWhen a next page's response is identical to the previous one, paging stops: the export records an open error with code `page_response_error` (\"Identical response received from next page request. No more page requests will be made.\") and the run completes with the pages already read, so later pages are never exported. A request that doesn't change between pages produces this, as observed with `method: page` and the pagination variable missing from the relative URI and body; reference the variable so each request asks for the next page.","properties":{"method":{"type":"string","description":"Pagination strategy used to request each subsequent page; match it to the mechanism documented by the target API. Determines which companion fields are required: `token` needs `path` and `pathLocation`, `url` needs `path`, `relativeuri` needs `relativeURI`, `body` needs `body`, while `linkheader` typically needs no extra configuration. Using the wrong method results in errors or incomplete data retrieval.","enum":["linkheader","page","skip","token","url","relativeuri","body"]},"page":{"type":"integer","description":"Starting page number for `method: page`. Set to 1 for APIs whose first page is not zero-indexed; when omitted, paging starts at 0. The value is incremented automatically for each subsequent page request."},"skip":{"type":"integer","description":"Starting offset for `method: skip`. Rarely needed — most APIs start at 0, and the value is incremented by the page size automatically for each subsequent request. Set only when the API requires a non-zero starting offset."},"token":{"type":"string","description":"Initial token for `method: token`. Leave empty for normal pagination — the first request is sent without a token, and subsequent tokens are extracted from each response via `path`. Set only to resume from a known token or for APIs that require a token on the first request."},"path":{"type":"string","description":"Location of the pagination value in each response — the continuation token for `method: token`, or the complete next-page URL for `method: url`. When `pathLocation` is `body`, use a dot-notation JSON path (e.g. `meta.nextToken`); when `header`, use the exact case-sensitive header name. Not used by other pagination methods."},"pathLocation":{"type":"string","description":"Where the export looks for the pagination token referenced by `path`. Required for `method: token`; not used by other pagination methods.","enum":["body","header"]},"pathAfterFirstRequest":{"type":"string","description":"Alternative token location used for responses after the first page, in the same format as `path`. Set only when the API moves the token to a different location in subsequent responses — setting it unnecessarily can cause pagination to fail."},"relativeURI":{"type":"string","description":"Overrides the main relative URI for second and later page requests; leave empty when the main relative URI works for all pages. Build it with handlebars using the `previous_page` context — `previous_page.full_response` (the prior response body), `previous_page.last_record` (the last record of the prior page), and `previous_page.headers` (the prior response headers)."},"body":{"type":"string","description":"Overrides the main request body for second and later page requests, typically for GraphQL or SOAP APIs that paginate through the body; leave empty when the main body works for all pages. Build it with handlebars using the `previous_page` context — `previous_page.full_response`, `previous_page.last_record`, and `previous_page.headers`."},"mergeBodyParamsToPagingBody":{"type":"boolean","description":"Only applies when `paging.method` is `body`. When true, the body\nparameters produced by the export's mappings at runtime are merged\ninto the evaluated `paging.body` for page 2+ requests, so paging\nrequests keep the same mapped parameters as the first page.\nOmitted/false keeps the original behavior — page 2+ requests send\nonly the evaluated `paging.body`."},"linkHeaderRelation":{"type":"string","description":"Link header relation followed for `method: linkheader` when the API uses a value other than the default `next`. Case-sensitive and must exactly match the `rel` value in the Link header, without the `rel=` prefix."},"resourcePath":{"type":"string","description":"Overrides the path to records for second and later page responses. Set only when follow-up pages place records at a different location than the first response; leave empty when all pages share the same structure."},"lastPageStatusCode":{"type":"integer","description":"HTTP status code that signals the last page, replacing the default behavior of treating 404 as the end of pagination. When this status is received (any code but `200` paired with `lastPagePath`), paging stops and the response is not treated as an error. Set it when the API uses a non-404 code (such as 204 or 400) to indicate no more pages, or to `200` together with `lastPagePath` when the last page is an ordinary 200 response: then the response is parsed, its records are kept, and paging stops only when `lastPagePath` matches `lastPageValues`."},"lastPagePath":{"type":"string","description":"JSON path to a response-body field that signals the end of pagination, such as a \"has more\" flag or a cursor that empties on the last page. Must be used with `lastPageValues`, which lists the values at this path that stop paging. If the path does not exist in a response, the condition is not considered met. With `method: token` the path is read only on a response whose status code equals `lastPageStatusCode`, so pair it with `lastPageStatusCode: 200` for an ordinary last page; with `method: body` it is read on every response."},"lastPageValues":{"type":"array","description":"Values at `lastPagePath` that stop pagination; a match on any entry ends paging. All entries are compared as exact, case-sensitive strings, even for boolean or numeric fields — use `\"true\"`, `\"false\"`, `\"null\"` for JSON null, or `\"\"` for an empty string. Required when `lastPagePath` is set.","items":{"type":"string"}},"maxPagePath":{"type":"string","description":"JSON path to the total page count in the response, used to stop paging once the last page is reached. Only applies to the `page` and `skip` methods. Point it at the total number of pages, not the current page number."},"maxCountPath":{"type":"string","description":"JSON path to the total record count in the response, used to stop paging once all records have been retrieved. Only applies to the `page` and `skip` methods; when both are set, `maxPagePath` takes precedence. Point it at the total number of records, not the count in the current page."}},"if":{"properties":{"method":{"const":"body"}},"required":["method"]},"then":{"required":["body"]},"else":{"if":{"properties":{"method":{"const":"token"}},"required":["method"]},"then":{"required":["path","pathLocation"]},"else":{"if":{"properties":{"method":{"const":"relativeuri"}},"required":["method"]},"then":{"required":["relativeURI"]},"else":{"if":{"properties":{"method":{"const":"url"}},"required":["method"]},"then":{"required":["path"]}}}}},"response":{"type":"object","description":"Controls how records are extracted from the API response and how success or failure is detected at the response level. When the API wraps records in an envelope object, set `resourcePath` to the path of the records array — without it, the entire response body is treated as a single record. Leave this object undefined when the API returns a bare JSON array.","properties":{"resourcePath":{"type":"string","description":"Dot-separated path to the array of records inside the response body (e.g. `data.customers` for `{\"data\": {\"customers\": [...]}}`); without it, a wrapped response is treated as a single record. Values starting with `$` are evaluated as JSONPath instead of dot notation — including unions (`$['orders','invoices'][*]`) and filter expressions (`$.items[?(@.qty>3)]`); all other values keep the existing dot-notation behavior unchanged. Leave undefined when the API returns a bare JSON array. This extracts records from the API response — not to be confused with `oneToMany`/`pathToMany`, which unwrap arrays from input records, or `paging.resourcePath`, which applies only to subsequent page responses."},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"},"resourceIdPath":{"type":"string","description":"Path to the unique identifier within each record, used primarily when processing results of asynchronous import responses. When omitted, the system looks for standard `id` or `_id` fields automatically."},"successPath":{"type":"string","description":"Path to a response field that signals whether the call succeeded, for APIs that return HTTP 200 even on failure. With `successValues`, the value at this path must match one of them; without `successValues`, the path only has to resolve — a response in which it is absent fails with `response_failure` (\"Unable to extract paths from response body using path: <path>, Response: …\")."},"successValues":{"type":"array","items":{"type":"string"},"description":"Values at `successPath` that mark the response as successful; any other value is treated as an error. All comparisons are string-based — use `\"true\"` or `\"false\"` for boolean fields."},"errorPath":{"type":"string","description":"Path to the error message in the response body. The value at this path is included in error logs and error records when the API returns an error; without it, the message is the whole response body."},"failPath":{"type":"string","description":"Path to a response field that signals failure even when the HTTP status code is 200 — the inverse of `successPath`. With `failValues`, the response fails when the value there matches one of them; without, when the path holds a truthy value. A path that resolves to an array matches only when the array has exactly one element."},"failValues":{"type":"array","items":{"type":"string"},"description":"Values at `failPath` that mark the response as failed, even when the HTTP status code is 200."},"allowArrayforSuccessPath":{"type":"boolean","description":"When true, treats the value at `successPath` as an array and counts the response as successful if any element matches `successValues`, rather than requiring a single scalar match. Set it for APIs that return per-record status arrays in a batch response."},"twoDArray":{"type":"object","description":"Parsing options for endpoints that return tabular data as a two-dimensional array (rows of cells) rather than an array of objects, such as spreadsheet-style or report APIs.","properties":{"hasHeader":{"type":"boolean","description":"When true, the first row is treated as column headers and used to name the fields of each generated record."},"doNotNormalize":{"type":"boolean","description":"When true, the rows are passed through as raw arrays instead of being normalized into keyed records."}}},"blobFormat":{"type":"string","description":"Controls how the binary response body is decoded for blob exports. Only relevant when `http.type` is `file` or the export type is `blob`.","enum":["utf8","ucs2","utf-16le","ascii","binary","base64","hex"]}}},"_httpConnectorVersionId":{"type":"string","format":"objectId","description":"Pins the export to a version of the published HTTP connector its connection is bound to\n(`connection.http._httpConnectorId`), so the UI renders that connector's endpoint form instead\nof a hand-authored request. Linked on save together with `_httpConnectorResourceId` and\n`_httpConnectorEndpointId` — see that field for the rule."},"_httpConnectorResourceId":{"type":"string","format":"objectId","description":"The connector resource (a group of related endpoints, such as Orders) that this export's\nendpoint belongs to. Linked on save together with `_httpConnectorEndpointId` — see that field\nfor the rule."},"sendAuthForFileDownloads":{"type":"boolean","description":"When true, includes authentication headers when downloading files."},"_httpConnectorEndpointId":{"type":"string","format":"objectId","description":"The connector endpoint this export calls, shown in the UI as the selected endpoint. On save the\nplatform derives it (with the version and resource ids) from `relativeURI` and `method` when\nthey match an endpoint of the connector the connection is bound to — path parameters, literal\nids and query strings still match — and replaces ids that belong to another connector. An id for\nthe connection's own connector is kept as sent even when the URI names a different endpoint;\nwhen nothing matches, or the connection has no connector, whatever was sent is kept and nothing\nis derived."}}},"Salesforce":{"type":"object","description":"Configuration for Salesforce exports. Required when the export's `_connectionId` references a Salesforce connection; omit for other connection types. The `type` field selects the extraction mode: `soql` runs batch queries and requires the `soql` object, while `distributed` listens for real-time events and requires the `distributed` object. File retrieval (when the export's `type` is `blob`) requires `sObjectType` and `id` instead.","properties":{"type":{"type":"string","description":"Selects the extraction mode and determines which configuration object is required. Use `soql` for scheduled or on-demand batch queries and lookups — it requires the `soql` object and works with both the `rest` and `bulk` APIs and the standard, delta, test, and once export types. Use `distributed` for real-time event handling — it requires the `distributed` object, always appears as a Listener in Flow Builder, ignores the `api` field, and supports only the standard export type.","enum":["soql","distributed"]},"sObjectType":{"type":"string","description":"API name of the Salesforce object the export operates on; the object must exist in the connected org and be accessible to the integration user. Required for distributed exports and for blob exports — blob exports accept only the file storage objects (`Attachment`, `ContentVersion`, `Document`) paired with the `id` field. Optional for SOQL exports, where the object can be inferred from the query."},"id":{"type":"string","description":"Salesforce record ID of the file to retrieve; required for blob exports and not used for `soql` or `distributed` exports. Accepts a static ID or a handlebars expression such as `{{record.Attachment_Id__c}}` to resolve the file at runtime — dynamic IDs require the export to run as a lookup (`isLookup: true`). The ID must belong to the object named in `sObjectType`."},"includeDeletedRecords":{"type":"boolean","description":"When true, SOQL exports use Salesforce's `queryAll()` instead of `query()`, including Recycle Bin records deleted within the past 15 days; each record's `IsDeleted` field identifies deletions. Useful for synchronizing deletes to target systems or maintaining a complete audit trail. Ignored for distributed and blob exports.","default":false},"api":{"type":"string","description":"Salesforce API used to run SOQL queries; ignored for distributed and blob exports. Use `rest` (the default when omitted) for smaller datasets (under 10,000 records) and for lookup exports — the Bulk API is not compatible with `isLookup: true`. Use `bulk` for large data volumes and higher throughput, optionally tuned through the `bulk` object.","enum":["rest","bulk"]},"bulk":{"type":"object","description":"Settings for Salesforce Bulk API 2.0 jobs. Only applies when `api` is `bulk` on a SOQL export; ignored otherwise.","properties":{"maxRecords":{"type":"integer","description":"Caps how many records a single Bulk API job retrieves, which helps prevent timeouts with complex queries or large records. When omitted, Salesforce's default applies. Lower values suit complex or custom objects; higher values improve throughput for simple records.","minimum":10000},"purgeJobAfterExport":{"type":"boolean","default":true,"description":"When true, deletes the Bulk API job in Salesforce after all data is retrieved, keeping the Bulk Data Load Jobs list clean — at the cost of making job details unavailable for later troubleshooting. Has no effect on the data retrieved or the success of the export."}}},"soql":{"type":"object","description":"SOQL query configuration; required when `type` is `soql` and not used otherwise. The query controls which objects, fields, and filter conditions the export retrieves, with either the REST or Bulk API.\n\nRequired when type is soql.","properties":{"query":{"type":"string","description":"SOQL statement passed directly to Salesforce that defines which objects, fields, and records the export retrieves. Supports handlebars — for delta exports, filter with `WHERE LastModifiedDate > {{lastExportDateTime}}` — and relationship queries that fetch parent and child records together. Select only the fields you need and use `ORDER BY` for consistent results across pages.","maxLength":200000}}},"distributed":{"type":"object","description":"Real-time listener configuration — the object that makes an export a listener. Set it, with `type: distributed`, when creating one; a listener saved from the integrator.io form carries it with no `type`, and the platform fires it either way. Not used on SOQL or file exports. Records reach the flow only when an Apex trigger on `sObjectType` in the connected org calls the Celigo package (`integrator_da__.RealTimeExporter.processExport()`) — no scheduling or manual execution is involved. Integration Apps create their triggers at installation; for a custom flow the customer deploys the trigger in Salesforce, starting from the code the export page shows under Required trigger.","properties":{"referencedFields":{"type":["array","null"],"description":"Fields from related objects to include in the exported record, written in dot notation (e.g. `Account.Name`, `Owner.Email`, `Custom_Lookup__r.Field__c`). Works with lookup and master-detail relationships, up to 10 unique referenced relationships per export. Referenced fields are retrieved via separate API calls, so include only fields the integration actually needs.","items":{"type":["string","null"]}},"disabled":{"type":"boolean","description":"When true, the listener stops processing events while its configuration and Salesforce triggers stay in place. Events that occur while disabled are ignored, not queued — they are not processed retroactively when re-enabled, so consider a delta export to catch up after extended pauses."},"qualifier":{"type":["string","null"],"description":"SOQL WHERE-clause condition that filters which record events the listener processes; when omitted (stored as `null`), every event for the object is processed. The Celigo Salesforce package evaluates it inside Salesforce as `SELECT Id FROM <sObjectType> WHERE (<qualifier>) AND Id IN :ids` before anything is sent, so only fields of the monitored object and SOQL comparison syntax apply — Salesforce formula functions are not part of this grammar. A deleted record is never returned by that query, so a listener with any qualifier receives no delete events; leave it unset when removals must trigger the flow."},"batchSize":{"type":["integer","null"],"description":"Controls how many event records are grouped into each real-time processing batch. Stored as `null` when not configured. Smaller batches lower latency for time-sensitive operations; larger batches improve throughput for high-volume objects. Does not limit how many records are processed in total — only how they are grouped.","minimum":4,"maximum":200},"skipExportFieldId":{"type":["string","null"],"description":"API name of a Salesforce checkbox field used to prevent infinite loops in bidirectional syncs: when the integration updates a record, this field is set so the resulting event is ignored, then cleared automatically. Required for bidirectional sync scenarios. The field must be a checkbox dedicated to integration use and updateable by the integration user."},"relatedLists":{"type":["array","null"],"description":"Child record sets to include with the parent record when it changes, one entry per related list. When omitted (stored as `null`), only the primary record is processed. Each related list adds Salesforce API calls and increases payload size.","items":{"type":"object","description":"Defines how to retrieve one type of child record related to the primary object. Configure multiple entries to retrieve different types of children.","properties":{"referencedFields":{"type":"array","description":"API names of the child object fields to retrieve; only the listed fields are included, and an empty array retrieves only the `Id` field. Include only fields the integration needs — each field adds data volume and processing time.","items":{"type":["string","null"]}},"parentField":{"type":"string","description":"API name of the lookup or master-detail field on the child object that references the parent (e.g. `AccountId`, `Parent_Object__c`) — the field's API name, not the relationship name. Used to build the query that fetches children for each parent record."},"sObjectType":{"type":"string","description":"Case-sensitive API name of the child object to retrieve (e.g. `Contact`, `Custom_Child__c`) — use the API name, not the label. The object must have a relationship field to the parent and be accessible to the connected user."},"filter":{"type":"string","description":"SOQL condition that limits which child records are included; when omitted, all related children are retrieved. Provide only the condition expression without the `WHERE` keyword — it is combined automatically with the parent relationship filter."},"orderBy":{"type":"string","description":"Sort order for the retrieved child records, as field names with optional `ASC`/`DESC` directions (e.g. `CreatedDate DESC`). Provide only the fields and directions without the `ORDER BY` keywords. When omitted, Salesforce determines the order."}},"required":["parentField","referencedFields","sObjectType"]}}}}},"if":{"properties":{"type":{"const":"distributed"}},"required":["type"]},"then":{"required":["distributed","sObjectType"]},"else":{"if":{"required":["distributed"]},"then":{},"else":{"if":{"required":["id"]},"then":{"required":["sObjectType"]},"else":{"if":{"not":{"propertyNames":{"enum":["metadata"]}}},"then":{"required":["soql","type"]}}}}},"AS2":{"type":"object","description":"Configures AS2 exports and listeners. Optional — the transport settings live on the AS2\nconnection, and an AS2 export is valid with only `file` (parse settings); set this object\nto link a Trading Partner Connector or to transfer raw files as blobs. An AS2 listener\nacts as the flow's source, receiving trading-partner transmissions in near real-time and\nhandling decryption, signature verification, and MDN generation; AS2 (Applicability\nStatement 2) transmits EDI and other data securely over HTTP/S using S/MIME encryption\nand digital signatures.","properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies the partner-specific EDI configuration —\ncommunication protocol, document schemas, mappings, validation rules, and endpoint\ndetails. Set this to link the export to all settings required for AS2 communication\nwith that partner."},"blob":{"type":"boolean","description":"When true, retrieves raw files without parsing them into structured records (rendered as\na \"Transfer\" step in the flow UI). Use only when the file contents are not needed in\nsubsequent steps — for binary files or when parsing is handled downstream. Only\navailable on AS2 and VAN exports."}},"required":[]},"DynamoDB":{"type":"object","description":"Defines how records are queried from DynamoDB tables. Required when the _connectionId field\nreferences a DynamoDB connection; must not be included for other connection types. Basic\nexports need region, method, tableName, keyConditionExpression, expressionAttributeNames,\nand expressionAttributeValues; once exports (export type \"once\") additionally need\nonceExportPartitionKey, plus onceExportSortKey for composite-key tables.","required":["region","tableName","keyConditionExpression","expressionAttributeNames","expressionAttributeValues"],"properties":{"region":{"type":"string","enum":["us-east-1","us-east-2","us-west-1","us-west-2","af-south-1","ap-east-1","ap-south-1","ap-northeast-1","ap-northeast-2","ap-northeast-3","ap-southeast-1","ap-southeast-2","ca-central-1","eu-central-1","eu-west-1","eu-west-2","eu-west-3","eu-south-1","eu-north-1","me-south-1","sa-east-1"],"description":"AWS region hosting the DynamoDB table. Must match the region where the table is\ndeployed so the integration can reach it.","default":"us-east-1"},"method":{"type":"string","enum":["query"],"description":"DynamoDB operation used to retrieve items. Only \"query\" is currently supported."},"tableName":{"type":"string","description":"Name of the DynamoDB table to query. Must exactly match an existing table (case-sensitive)."},"keyConditionExpression":{"type":"string","description":"Key condition determining which items the query retrieves. Must include a condition on\nthe partition key and may add sort-key conditions (equality, BETWEEN, begins_with).\nReference attribute names with \"#\" placeholders defined in expressionAttributeNames and\nvalues with \":\" placeholders defined in expressionAttributeValues."},"filterExpression":{"type":"string","description":"Filters query results on non-key attributes after the key condition is applied; omit to\nreturn all items matching the key condition. Uses the same \"#\" and \":\" placeholders\ndefined in expressionAttributeNames and expressionAttributeValues."},"projectionExpression":{"type":"array","items":{"type":"string"},"description":"Attributes to return from each item, reducing data transfer; omit to return all\nattributes. Each array element is one field, referenced via \"#\" placeholders defined in\nexpressionAttributeNames."},"expressionAttributeNames":{"type":"string","description":"JSON string mapping \"#\" placeholders to actual attribute names, e.g.\n{\"#pk\": \"customerId\"}. Placeholders defined here are used in keyConditionExpression,\nfilterExpression, and projectionExpression."},"expressionAttributeValues":{"type":"string","description":"JSON string mapping \":\" placeholders to comparison values, e.g. {\":status\": \"ACTIVE\"}.\nValues can be static or dynamic handlebars expressions such as {{lastExportDateTime}},\nand are referenced from keyConditionExpression and filterExpression."},"onceExportPartitionKey":{"type":"string","description":"Partition key attribute that uniquely identifies items when the export's type is \"once\".\nCeligo uses it to mark items as processed after a successful export, preventing the same\nitems from being exported again on subsequent runs."},"onceExportSortKey":{"type":"string","description":"Sort key attribute used together with onceExportPartitionKey to identify processed items\nwhen the table has a composite primary key. Omit for tables keyed by a partition key\nalone."},"pathToRecords":{"$ref":"#/components/schemas/PathToRecords"},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"}}},"PathToRecords":{"type":"string","maxLength":250,"description":"Path to the array of records inside each document or item fetched from the source; each array element is emitted as an individual record, which keeps records within the 5 MB page-size limit even when the source document exceeds it. Accepts dot notation (e.g. `data.orders`) or JSONPath for values starting with `$`, including unions (`$['orders','invoices'][*]`) and filter expressions (`$.items[?(@.qty>3)]`); a path that resolves to nothing emits zero records for that document or item. When omitted, each document or item is emitted as one record, exactly as before — HTTP and file-based exports configure this same capability through their existing `http.response.resourcePath` and `file.json`/`file.xml` `resourcePath` fields."},"FTP":{"type":"object","description":"Defines which files to retrieve from an FTP, FTPS, or SFTP server. Required when the\n_connectionId field references an FTP/SFTP connection; must not be included for other\nconnection types. directoryPath selects the folder, fileNameStartsWith/fileNameEndsWith\nfilter files by name, and backupDirectoryPath controls where files are moved after\nretrieval.","required":["directoryPath"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies partner-specific B2B settings for this export.\nWhen set, the export inherits the connector's pre-configured settings; omit to use only\nthe FTP connection details."},"directoryPath":{"type":"string","description":"Directory on the server to retrieve files from, either absolute or relative to the login\ndirectory; the FTP user must have read permission on it. Use forward slashes regardless\nof server OS — paths are case-sensitive on UNIX/Linux servers. Supports handlebars\ntemplates, e.g. archive/{{date 'YYYY-MM-DD'}}."},"fileNameStartsWith":{"type":"string","description":"Only retrieves files whose names start with this value (case-sensitive on most servers);\naccepts static text or handlebars templates. When combined with fileNameEndsWith, files\nmust match both."},"fileNameEndsWith":{"type":"string","description":"Only retrieves files whose names end with this value, commonly a file extension\n(case-sensitive on most servers). When combined with fileNameStartsWith, files must\nmatch both."},"backupDirectoryPath":{"type":"string","description":"Directory on the same server where files are moved after successful export, giving you\nan independent backup; if omitted, files are simply deleted from the source directory\nafter successful export unless `file.skipDelete` is true (Celigo also keeps its own copy\nof processed files for a set period). Supports static paths or handlebars templates."}}},"JDBC":{"type":"object","description":"Configuration object for JDBC (Java Database Connectivity) data integration exports.\n\nThis object is REQUIRED when the _connectionId field references a JDBC database connection\nand must not be included for other connection types. It defines how data is extracted\nfrom relational databases using SQL queries.\n\n**Jdbc export capabilities**\n- Execute custom SQL SELECT statements\n- Support for joins, aggregations, and functions\n- Flexible data retrieval from any accessible tables or views\n- Compatible with all major database systems\n**Critical:** WHAT BELONGS IN THIS OBJECT\n- `query` - **ALWAYS REQUIRED** - The SQL SELECT statement\n- `once` - **REQUIRED** when the export's Object Type is `\"once\"` (set _include_once: true)\n- **DO NOT** put `delta` inside this object - delta is handled via the query\n\n**Delta exports (type: \"delta\")**\nFor delta/incremental exports, do NOT populate a `delta` object inside `jdbc`.\nInstead, use `{{lastExportDateTime}}` or `{{currentExportDateTime}}` directly in the query:\n```json\n{\n  \"type\": \"delta\",\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE updatedAt > {{lastExportDateTime}}\"\n  }\n}\n```\n\n**Once exports (type: \"once\")**\nFor once exports (mark records as processed), populate `jdbc.once.query`:\n```json\n{\n  \"type\": \"once\",\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE exported = false\",\n    \"once\": {\n      \"query\": \"UPDATE customers SET exported = true WHERE id = {{record.id}}\"\n    }\n  }\n}\n```\n\n**Standard exports (type: null or not specified)**\nJust provide the query:\n```json\n{\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE status = 'ACTIVE'\"\n  }\n}\n```","required":["query"],"properties":{"formType":{"type":["string","null"],"enum":["sql","simple",null],"description":"Selects how the export's query is authored. Use `sql` for a hand-written SQL statement in\n`query`; use `simple` to build the query from the `simple` object's table/columns/filter."},"query":{"type":"string","description":"SQL SELECT statement executed to retrieve data, from simple table selections to joins and\naggregations. For delta exports, include {{lastExportDateTime}}/{{currentExportDateTime}}\nin the WHERE clause rather than configuring a separate delta object."},"once":{"type":"object","description":"**CRITICAL: REQUIRED when the export's Object Type is \"once\".**\n\nIf Object Type is \"once\", you MUST set _include_once to true (or include this object).\n\nThis object has ONLY ONE property: \"query\" (a SQL UPDATE string).\nDO NOT create any other properties like \"update\", \"table\", \"set\", \"where\", etc.\n\nCORRECT format:\n```json\n{\"query\": \"UPDATE customers SET exported=true WHERE id={{record.id}}\"}\n```\n\nWRONG format (DO NOT DO THIS):\n```json\n{\"update\": {\"table\": \"customers\", \"set\": {...}}}\n```\n","properties":{"query":{"type":"string","description":"**REQUIRED** - A SQL UPDATE statement string to mark records as processed.\n\nThis is a plain SQL UPDATE query string, NOT a structured object.\n\nThe query runs AFTER each record is successfully exported, setting a flag\nto indicate the record has been processed.\n\nFormat: \"UPDATE <table> SET <column>=<value> WHERE <id_column>={{record.<id_field>}}\"\n\nExample: \"UPDATE customers SET exported=true WHERE id={{record.id}}\"\n\nThe {{record.id}} placeholder is replaced with the actual record ID from each exported row.\n"}}},"simple":{"type":"object","description":"Visual query-builder configuration used when `formType` is `simple`; the platform builds\nthe SELECT from these instead of raw SQL. Only relevant when `formType` is `simple`.","properties":{"table":{"type":"string","description":"Table or view to select rows from."},"columns":{"type":"array","items":{"type":"string"},"description":"Columns to return; an empty list selects all columns."},"filter":{"description":"Filter applied as the WHERE clause for the generated query, stored as the\nplatform's structured filter-rules object.","allOf":[{"$ref":"#/components/schemas/Filter"}]}}}}},"MongoDB":{"type":"object","description":"Defines how documents are retrieved from MongoDB collections. Required, with `method` and\n`collection`, when the _connectionId field references a MongoDB connection and the export\nfetches documents; must not be included for other connection types. A change-stream\nlistener (`type: stream`) is configured through `cdc` instead and may omit this object, or\nset only `collection` to scope the listener. Supports find queries with optional filter\ncriteria and field projections.","properties":{"method":{"type":"string","enum":["find"],"description":"MongoDB operation used to retrieve documents. Only \"find\" is currently supported,\nequivalent to db.collection.find(filter, projection)."},"collection":{"type":"string","description":"Name of the MongoDB collection to query. Case-sensitive and must reference an existing\ncollection in the connected database. Required on find exports; on a change-stream\nlistener (`type: stream`) it is optional and may list several collections separated by\ncommas, matching the listener form's \"Collections\" field."},"filter":{"type":"string","description":"MongoDB query document, as a JSON string, that selects which documents to export; omit\nto return every document in the collection. Supports standard MongoDB query operators\nand handlebars variables for dynamic values — for example\n{\"lastModified\": {\"$gt\": \"{{lastRun}}\"}} for incremental processing."},"projection":{"type":"string","description":"MongoDB projection document, as a JSON string, that limits which fields are returned;\nomit to return all fields. Use 1 to include fields or 0 to exclude them — the two modes\ncannot be mixed except for _id, which is included by default unless explicitly excluded.\nProjection affects only the shape of returned documents, not which documents match."},"pipeline":{"type":"string","description":"MongoDB aggregation pipeline, as a JSON-array string of stages, used instead of a plain\nfind when documents need to be transformed, joined, or grouped server-side (e.g. `$match`,\n`$lookup`, `$group`). When set, it takes the place of `filter`/`projection`. Supports\nhandlebars variables for incremental processing."},"readPreference":{"type":"string","enum":["doNotOverride","primary","primaryPreferred","secondary","secondaryPreferred","nearest"],"default":"doNotOverride","description":"Overrides which replica-set member this export reads from, trading consistency for load\ndistribution. Leave at `doNotOverride` to inherit the connection's setting."},"pathToRecords":{"$ref":"#/components/schemas/PathToRecords"},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"}}},"NetSuite":{"type":"object","description":"NetSuite-specific export configuration. Required when `_connectionId` references a NetSuite\nconnection; omit for all other connection types. Supports saved-search, RESTlet, and\ndistributed (SuiteApp) exports, plus two file cabinet modes: blob exports transfer files\nas-is (set the export's top-level `type` to `blob` and configure `netsuite.blob`), while\nfile exports parse file contents into records (leave the export's top-level `type` unset\nand configure `netsuite.file`).","properties":{"type":{"type":"string","enum":["search","basicSearch","metadata","selectoption","restlet","getList","getServerTime","distributed","file"],"description":"Controls how data is retrieved from NetSuite and which sibling object must be configured:\n`search` pairs with `netsuite.searches`, `restlet` with `netsuite.restlet`, `distributed`\nwith `netsuite.distributed`, and `file` with `netsuite.file.folderInternalId`. For blob\nexports (raw file transfer without parsing), leave this field unset, set the export's\ntop-level `type` to `blob`, and configure `netsuite.internalId` instead. For lookups\n(`isLookup: true`), prefer `restlet`, which supports `suiteapp2.0` saved searches with\ndynamic inputs; `search` is limited for dynamic lookups."},"searches":{"type":"array","description":"Saved search configurations that query NetSuite for records. Each entry references a\nsaved search by internal ID, names the record type being searched, and can add filter\ncriteria.","items":{"type":"object","properties":{"savedSearchId":{"type":"string","description":"Internal ID of the NetSuite saved search to run."},"recordType":{"type":"string","description":"NetSuite record type being searched, as its exact lowercase script ID\n(e.g. \"customer\", \"salesorder\") — not the display name."},"criteria":{"type":"array","description":"Additional filter criteria applied on top of the saved search.","items":{"type":"object","properties":{"field":{"type":"string","description":"Script-id of the search column to filter on.  REQUIRED.\nMust come from the record type's SEARCH COLUMNS list\n(NOT the body-fields list).  Becomes the `name`\nargument of NetSuite's `search.createFilter()`."},"operator":{"type":"string","description":"Comparison operator.  REQUIRED.  Supported values\ndepend on the column's data type (e.g. `is`,\n`isnot`, `after`, `before`, `anyof`,\n`noneof`, `contains`, `startswith`,\n`greaterthan`, `lessthan`).  Must be a string\nNetSuite recognises -- mismatched operators cause\nruntime rejection."},"join":{"type":"string","description":"Optional join name when filtering through a related\nrecord (e.g. `customer` from a sales order).  Use\nonly when the column lives on a related record and\nNetSuite supports the join from the base record type."},"searchValue":{"description":"Value (or array of values for `anyof`-style\noperators) to compare against the column.  Required\nfor most operators; omit for the few unary operators\n(e.g. `isempty`, `isnotempty`)."}},"required":["field","operator"]}}}}},"metadata":{"type":"object","description":"Supplemental metadata associated with the NetSuite export."},"selectoption":{"type":"object","description":"Configuration for `selectoption` exports, which retrieve the available select options\nfor a NetSuite field."},"customFieldMetadata":{"type":"object","description":"Metadata describing the custom fields defined in the NetSuite account."},"skipGrouping":{"type":"boolean","description":"When true, each saved-search result row becomes its own record. When false or omitted,\nconsecutive result rows that share the record's internal id are grouped into one record\nshaped as an array of rows — the *Group rows* option in the UI."},"statsOnly":{"type":"boolean","description":"When true, returns only summary statistics about matching records instead of the\ndetailed records themselves."},"internalId":{"type":"string","description":"Internal ID of the file in the NetSuite file cabinet to export. Required for blob\nexports (the export's top-level `type` is `blob`); the file is transferred as-is\nwithout parsing. For parsed file exports, use `netsuite.file.folderInternalId` instead."},"blob":{"type":"object","properties":{"purgeFileAfterExport":{"type":"boolean","description":"When true, permanently deletes the file from the NetSuite file cabinet after a\nsuccessful export. When false or omitted, the file is left in place."}},"description":"Configuration for blob exports, which transfer files from the NetSuite file cabinet\nas-is without parsing them into records. To export a blob, set the export's top-level\n`type` to `blob`, set `netsuite.internalId` to the file's internal ID, and leave\n`netsuite.type` unset. Use `netsuite.file` instead when file contents should be parsed\ninto records."},"restlet":{"type":"object","properties":{"recordType":{"type":"string","description":"NetSuite record type the RESTlet operates on, as its script ID. Custom record\ntypes use the \"customrecord_\" prefix."},"batchSize":{"type":"number","description":"Number of records the RESTlet returns per request (default 1000). This, not the\nexport's `pageSize`, sizes each NetSuite call; the export keeps requesting until the\nsearch is exhausted. Larger batches reduce round-trips but raise NetSuite governance\nand memory use per call; tune to the record's size. The RESTlet caps a call at 1000,\nso a larger value acts as 1000. In a test run or preview the request is capped at the\nexport's `pageSize` (default 20) instead."},"searchId":{"type":["string","null"],"description":"Internal ID of the NetSuite saved search the RESTlet executes. Results follow the\nsaved search's current definition, so editing the search in NetSuite changes the\nexport output without changing this ID."},"useSS2Restlets":{"type":"boolean","description":"When true, calls SuiteScript 2.0 RESTlets. When false or omitted, legacy\nSuiteScript 1.0 RESTlets are used.\nSet at step creation together with restletVersion — the same\ncreation-time-only constraint applies (see restletVersion)."},"restletVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"RESTlet version the export invokes. Defaults to `suiteapp2.0` when\n`useSS2Restlets` is true, `suitebundle` otherwise.\nThe version is fixed when the step is created — the Advanced selector is\ndisabled on existing steps, and migrating an existing export to a\ndifferent version means recreating or cloning it."},"criteria":{"type":"array","description":"Filter conditions added on top of the saved search (the UI's \"Additional search\ncriteria\"). Each condition pairs a search column with an operator and one or two\ncomparison values, and the conditions are combined with AND — every one must match.","items":{"type":"object","properties":{"field":{"type":"string","description":"Script-id of the search column to filter on. This is the primary\nfield the criterion is evaluated against (e.g. \"trandate\", \"status\")."},"join":{"type":"string","description":"Join relationship that applies this filter to a related record's fields instead\nof the base record. Must match a join name NetSuite defines for the record type."},"operator":{"type":"string","enum":["after","allof","any","anyof","before","between","contains","doesnotcontain","doesnotstartwith","equalto","greaterthan","greaterthanorequalto","haskeywords","is","isempty","isnot","isnotempty","lessthan","lessthanorequalto","noneof","notafter","notallof","notbefore","notbetween","notequalto","notgreaterthan","notlessthan","notlessthanorequalto","noton","notonorafter","notonorbefore","notwithin","on","onorafter","onorbefore","startswith","within"],"description":"Comparison operator applied between the column and the search value, using\nNetSuite's lowercase search operator names. Which operators a column accepts\ndepends on its data type."},"searchValue":{"description":"Value the column is compared against — a string, a number, or an array of\nvalues for `anyof`-style operators. On lookup steps it may be a Handlebars\nexpression rendered from the incoming record (for example `{{poNumber}}`)."},"searchValue2":{"description":"Second comparison value for the two-value operators `between` and `notbetween`\n(the upper bound). Omit for every other operator."},"formula":{"type":"string","description":"NetSuite formula expression used as the filter condition, for criteria that\nstandard field-operator-value comparisons can't express."}}}},"columns":{"type":"array","description":"Search columns the RESTlet returns for each matching record. With a saved search,\nthe RESTlet returns the search's own result columns followed by any listed here: a\nlist adds columns and never removes one of the search's, so remove a column in the\nsaved search itself. An ad-hoc search (no `searchId`) must list its columns here.","items":{"type":"object","properties":{"_id":{"type":"string","readOnly":true,"description":"Subdocument id the server assigns to the column entry on save. Not a NetSuite\nfield id — never set it when authoring."},"name":{"type":"string","description":"Column field name as referenced in the search."},"join":{"type":"string","description":"Join relationship that sources this column from a related record instead of\nthe base record. Supports dot notation for nested joins (e.g. \"employee.manager\")."},"summary":{"type":"string","enum":["group","sum","count","avg","min","max"],"description":"Summary type applied to this column, turning the search into a summary search."},"formula":{"type":"string","description":"NetSuite formula expression that computes this column's value at runtime, such\nas a CASE statement or date formatting, instead of reading a stored field."},"label":{"type":"string","description":"Display name for the column; it becomes the property name of the column's value\nin each exported record."},"sort":{"type":"boolean","description":"When true, query results are sorted by this column's values."}}}},"markExportedBatchSize":{"type":"integer","description":"Number of records updated per RESTlet call when marking records as exported in\nNetSuite (live: an integer count, e.g. 100).","maximum":100,"minimum":1},"hooks":{"type":["object","null"],"properties":{"batchSize":{"type":"number","description":"Legacy location of the RESTlet page size; `restlet.batchSize` is the current\nfield and takes precedence when both are set."},"preSend":{"type":"object","properties":{"fileInternalId":{"type":"string","description":"Internal ID of the JavaScript file in the NetSuite File Cabinet that defines\nthe hook function. Must be readable by the role behind the export's connection."},"function":{"type":"string","description":"Name of the global function in that file that NetSuite invokes as the pre\nsend hook."},"configuration":{"type":"object","description":"Free-form JSON stored with the hook reference for the script's own use. Custom\nsettings in scope for the export are passed to the function separately as\n`options.settings`."}},"description":"SuiteScript hook that runs inside NetSuite after a page of records is collected\nand before it is sent to the Celigo platform."}},"description":"SuiteScript hooks for the export. Only `preSend` exists on exports; imports carry\nthe pre map, post map, and post submit hooks."},"cLocked":{"type":"object","description":"Lock state that prevents modification of the configuration when set."}},"description":"Configuration for `restlet` exports. Identifies the RESTlet script that retrieves data\nfrom NetSuite, along with the saved search, criteria, and columns it executes with."},"distributed":{"type":"object","properties":{"recordType":{"type":"string","description":"NetSuite record type the listener is deployed on, as its lowercase script ID\n(`customer`, `salesorder`, `customrecord_<scriptid>`) — never the display name or a\ncustom record's numeric internal ID. Types the platform's record-type catalog marks\n`doesNotSupportRTExport` cannot host a listener and are not offered in the UI."},"executionContext":{"type":"array","description":"NetSuite execution contexts that trigger this distributed export. A record change\nfires the export only when it occurs in one of the listed contexts.","default":["userinterface","webstore"],"items":{"type":"string","enum":["userinterface","webservices","csvimport","offlineclient","portlet","scheduled","suitelet","custommassupdate","workflow","webstore","userevent","mapreduce","restlet","webapplication","restwebservices"]}},"disabled":{"type":"boolean","description":"When true, the listener's NetSuite deployment is inactive and record saves no longer\ntrigger the export. Maintained by the platform as the flow is disabled and re-enabled;\nre-enabling the flow re-registers the deployment. A listener is created disabled, and\na flow created already enabled leaves it that way until a flow update sets\n`disabled: false`, which activates it within seconds."},"executionType":{"type":"array","description":"Record operations that trigger this distributed export. A record event fires the\nexport only when its operation matches one of the listed types.","default":["create","edit","xedit"],"items":{"type":"string","enum":["create","edit","delete","xedit","copy","view","cancel","approve","reject","pack","ship","markcomplete","reassign","editforecast","dropship","specialorder","orderitems","paybills","print","email"]}},"qualifier":{"type":["array","string","null"],"description":"Field-specific qualification criteria the SuiteApp evaluates against the record before\nexporting it; saves that do not match are discarded. Stored as a filter-expression array\nof `[field, operator, value]` rules joined by lowercase `\"and\"` / `\"or\"`, as a legacy\nexpression string, or null when no criteria are set."},"skipExportFieldId":{"type":"string","description":"Script ID of a checkbox body field on the record type. A save with that box checked is\nskipped by the listener, which then clears the box itself — used to keep a mass update\nfrom fanning out and to stop a write-back import from re-triggering the listener."},"hooks":{"type":"object","description":"SuiteScript hook that runs inside NetSuite before the record leaves it.","properties":{"preSend":{"type":"object","description":"Pre-send hook: a SuiteScript function in the NetSuite file cabinet invoked before\nrecords leave NetSuite, with the full SuiteScript API available — for example to run\ndynamic searches against the record being exported.","properties":{"fileInternalId":{"type":"string","description":"Internal ID of the file cabinet file that holds the hook's SuiteScript."},"function":{"type":"string","description":"Name of the function in that file to invoke for each export."},"configuration":{"type":"object","description":"Settings passed to the hook function."}}}}},"sublists":{"type":["array","null"],"description":"IDs of the NetSuite sublists to include with each exported record (for example `item`\nfor transaction line items). Records are exported with body fields only when no\nsublists are selected. Legacy documents may store null (equivalent to no sublists).","items":{"type":"string"}},"referencedFields":{"type":"object","description":"Field references recorded on the listener by the platform. Not exposed in the listener\nform; leave as stored."},"relatedLists":{"type":"object","description":"Related record lists recorded on the listener by the platform. Not exposed in the\nlistener form; leave as stored."},"forceReload":{"type":"boolean","description":"When true, the listener reloads the record from the database after the save before\nexporting it, so fields that are populated only once the save completes — such as\ntransaction line IDs on new lines — are included. The reload slows every save of that\nrecord type."},"ioEnvironment":{"type":"string","description":"integrator.io environment the listener's NetSuite deployment reports to. Written by the\nplatform when the listener registers with NetSuite; live-observed values include\n`production` and `staging`."},"ioDomain":{"type":"string","description":"integrator.io domain the listener's NetSuite deployment calls back to — the account's\nown regional domain, stamped by the platform on every save and echoed on create and\nupdate responses; not returned by GET."},"lastSyncedDate":{"type":"string","format":"date-time","description":"When the listener's deployment last synchronized with NetSuite. Written by the platform;\nnot set until the first successful synchronization."},"settings":{"type":"object","description":"Additional key-value settings for the distributed export. Not exposed in the listener\nform."},"useSS2Framework":{"type":"boolean","description":"Derived by the platform from `frameworkVersion` — true when it is `suiteapp2.0`, absent\notherwise — and echoed on create and update responses; not returned by GET. A value\nsent on create or update is replaced by the derived one."},"frameworkVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"SuiteApp framework version used by the distributed export.\nFixed when the step is created — the Advanced selector is disabled on\nexisting steps, and migrating an existing listener to a different\nversion means recreating or cloning it."}},"description":"Configuration for `distributed` exports, which use the NetSuite SuiteApp to fire\nreal-time, event-driven exports when records change. Define the record type to listen\nto and the execution contexts and operations that trigger it."},"getList":{"type":"array","description":"Configuration for `getList` exports — a list of record references, each retrieving one\nNetSuite record by its identifier. The export returns the fetched records in list order.","items":{"type":"object","properties":{"type":{"type":"string","description":"Standard NetSuite record type to retrieve (e.g. \"customer\", \"salesOrder\").\nUsed when `typeId` is absent. Case-sensitive."},"typeId":{"type":"string","description":"Script ID of a custom record type (\"customrecord_...\") or custom transaction\ntype (\"customtransaction_...\"). Takes precedence over `type` when present.\nCase-sensitive."},"internalId":{"type":"string","description":"Internal ID of the NetSuite record to retrieve. Assigned by NetSuite at record\ncreation and unique within the record type. Takes precedence over `externalId`."},"externalId":{"type":"string","description":"Identifier assigned by an external system, used to reference and synchronize the\nrecord across systems. Distinct from the NetSuite-assigned internal ID."}}}},"searchPreferences":{"type":"object","properties":{"bodyFieldsOnly":{"type":"boolean","description":"When true, search results include only the record's body fields and exclude fields\nfrom joined or related records. Reduces payload size when related data isn't needed."},"pageSize":{"type":"number","description":"Number of search results NetSuite returns per page."},"returnSearchColumns":{"type":"boolean","description":"When true, search results include the column data defined in the search. When\nfalse, column data is omitted and results contain only minimal record information."}},"description":"Preferences that control how NetSuite executes searches and shapes the results it returns."},"file":{"type":"object","description":"Configuration for file exports, which retrieve files from the NetSuite file cabinet\nand parse their contents (CSV, XML, JSON) into records. Leave the export's top-level\n`type` unset when using this object; for raw transfers without parsing, use\n`netsuite.blob` with the export's `type` set to `blob` instead.","properties":{"folderInternalId":{"type":"string","description":"Internal ID of the NetSuite file cabinet folder to export files from. Accepts a\nhandlebars expression (e.g. `{{record.folderId}}`) when the folder must be\nselected dynamically from record data. The ID is stable across folder renames and\nmoves, but may differ between non-production and production accounts."},"backupFolderInternalId":{"type":"string","description":"Internal ID of the NetSuite file cabinet folder where backup files are stored.\nIDs are account-specific and do not transfer between non-production and production\nenvironments."},"fileNameStartsWith":{"type":"string","description":"Optional prefix filter. Only files whose names start with this string are\nexported from the configured folder."},"fileNameEndsWith":{"type":"string","description":"Optional suffix filter. Only files whose names end with this string (e.g. a\n\".csv\" extension) are exported from the configured folder."}}}},"required":[]},"RDBMS":{"type":"object","description":"Configuration object for Relational Database Management System (RDBMS) data integration exports.\n\nThis object defines how data is read from a relational database and must not be included\nfor other connection types. For query-type exports (standard, delta, once) it is REQUIRED\nwhen the _connectionId field references an RDBMS database connection and holds the SQL\nquery. For real-time stream listeners (`type: \"stream\"`) it holds the CDC watch list\n(`tables`) instead — and a PostgreSQL listener scoped by a publication\n(`cdc.publicationName`) may omit this object entirely.\n\n**Rdbms export capabilities**\n- Execute custom SQL SELECT statements\n- Support for joins, aggregations, and functions\n- Flexible data retrieval from any accessible tables or views\n- Compatible with all major database systems\n\n**Critical:** WHAT BELONGS IN THIS OBJECT\n- `query` - The SQL SELECT statement - required for every query-type export (standard, delta, once)\n- `once` - **REQUIRED** when the export's Object Type is `\"once\"` (set _include_once: true)\n- `tables` - the CDC listener watch list - real-time stream listeners (`type: \"stream\"`) only\n- **DO NOT** put `delta` inside this object - delta is handled via the query\n- **DO NOT** author `query` on a stream listener - real-time CDC exports have no SQL query\n\n**Delta exports (type: \"delta\")**\nFor delta/incremental exports, do NOT populate a `delta` object inside `rdbms`.\nInstead, use `{{lastExportDateTime}}` or `{{currentExportDateTime}}` directly in the query, and\nset the export's top-level `delta.startDate`: without it the first run's window is empty.\n```json\n{\n  \"type\": \"delta\",\n  \"delta\": { \"startDate\": \"2026-01-01T00:00:00.000Z\" },\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE updatedAt > {{lastExportDateTime}}\"\n  }\n}\n```\n\n**Once exports (type: \"once\")**\nFor once exports (mark records as processed), populate `rdbms.once.query`:\n```json\n{\n  \"type\": \"once\",\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE exported = false\",\n    \"once\": {\n      \"query\": \"UPDATE customers SET exported = true WHERE id = {{record.id}}\"\n    }\n  }\n}\n```\n\n**Standard exports (type: null or not specified)**\nJust provide the query:\n```json\n{\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE status = 'ACTIVE'\"\n  }\n}\n```\n\n**Real-time CDC listener exports (type: \"stream\")**\nSQL Server and PostgreSQL connections support change-data-capture listeners that stream row\nchanges continuously (the `cdc` object holds the listener settings). A stream export has NO\n`query` — it watches `tables`:\n```json\n{\n  \"type\": \"stream\",\n  \"rdbms\": { \"tables\": \"public.orders,public.customers\" },\n  \"cdc\": { \"properties\": [{ \"name\": \"snapshot.mode\", \"value\": \"no_data\" }] }\n}\n```\nA PostgreSQL listener can instead scope tables through a publication\n(`cdc.publicationName`), in which case `tables` stays unset and the `rdbms` object may be\nomitted entirely.","properties":{"tables":{"type":"string","description":"Comma-separated list of tables the CDC listener watches for changes. Used only by real-time\nlistener exports (`type` is `stream`); standard SQL exports use `query` instead. SQL Server\nlisteners use fully-qualified three-part names (`database.schema.table`); PostgreSQL\nlisteners use two-part names (`schema.table`)."},"query":{"type":"string","description":"SQL SELECT statement executed to retrieve data, from simple table selections to joins,\naggregations, and parameterized queries with Handlebars expressions. Query-type exports\nonly — real-time stream listeners (`type` is `stream`) have no SQL query. For delta exports,\nreference {{lastExportDateTime}}/{{currentExportDateTime}} directly in the query rather\nthan configuring a separate delta object.\n\nDouble braces render a string as a quoted SQL literal with its apostrophes escaped, and\ntriple braces insert a value raw. Observed in Snowflake queries:\n\n- String helper output (`dateFormat`, `jsonSerialize`) and both delta variables are\n  quoted the same way; a number or boolean renders bare, whether it is a JSON value or a\n  numeric helper's result (`{{add 2 3}}` renders `5`).\n- In a run, {{lastExportDateTime}} and {{currentExportDateTime}} arrive as quoted ISO 8601\n  UTC literals with milliseconds (`'2026-01-15T00:00:00.000Z'`), or in `delta.dateFormat`\n  when it is set: add no quotes of your own, and don't parse them with a format mask\n  written for another format.\n- A preview of an unsaved export document renders both as empty, unquoted text, so a\n  delta query fails to compile there unless the document carries both in `postData`.\n- The quoting leaves backslashes, which Snowflake reads as escape sequences, so a\n  double-brace value holding a backslash arrives changed. Pass such a value in a\n  dollar-quoted constant with triple braces (`$${{{record.field}}}$$`), which Snowflake\n  reads as is, but only when it can't contain `$$`: a `$$` ends the constant, and the rest\n  is read as SQL."},"once":{"type":"object","description":"**CRITICAL: REQUIRED when the export's Object Type is \"once\".**\n\nIf Object Type is \"once\", you MUST set _include_once to true (or include this object).\n\nThis object has ONLY ONE property: \"query\" (a SQL UPDATE string).\nDO NOT create any other properties like \"update\", \"table\", \"set\", \"where\", etc.\n\nCORRECT format:\n```json\n{\"query\": \"UPDATE customers SET exported=true WHERE id={{record.id}}\"}\n```\n\nWRONG format (DO NOT DO THIS):\n```json\n{\"update\": {\"table\": \"customers\", \"set\": {...}}}\n```\n","properties":{"query":{"type":"string","description":"**REQUIRED** - A SQL UPDATE statement string to mark records as processed.\n\nThis is a plain SQL UPDATE query string, NOT a structured object.\n\nThe query runs AFTER each record is successfully exported, setting a flag\nto indicate the record has been processed.\n\nFormat: \"UPDATE <table> SET <column>=<value> WHERE <id_column>={{record.<id_field>}}\"\n\nExample: \"UPDATE customers SET exported=true WHERE id={{record.id}}\"\n\nThe {{record.id}} placeholder is replaced with the actual record ID from each exported row.\n"}}}}},"S3":{"type":"object","description":"Defines which files to retrieve from an Amazon S3 bucket. Required when the _connectionId\nfield references an AWS S3 connection; must not be included for other connection types.\nregion and bucket locate the source, keyStartsWith/keyEndsWith filter objects by key, and\nbackupBucket with keyPrefix controls where files are moved after retrieval.","required":["region","bucket"],"properties":{"region":{"type":"string","default":"us-east-1","description":"AWS region where the bucket is located. Case-insensitive; the value is normalized to\nlowercase."},"bucket":{"type":"string","description":"S3 bucket to retrieve files from. The connection's AWS credentials must have\ns3:ListBucket and s3:GetObject permissions on it."},"keyStartsWith":{"type":"string","description":"Only retrieves objects whose keys start with this value (case-sensitive), effectively\nselecting a folder in S3's flat key structure. When combined with keyEndsWith, objects\nmust match both."},"keyEndsWith":{"type":"string","description":"Only retrieves objects whose keys end with this value (case-sensitive), commonly a file\nextension. When combined with keyStartsWith, objects must match both."},"backupBucket":{"type":"string","description":"Bucket in the same region where files are moved after successful export, giving you an\nindependent backup; if omitted, files are simply deleted from the source bucket after\nsuccessful export unless `file.skipDelete` is true (Celigo also keeps its own copy of\nprocessed files for a set period). The connection's AWS credentials must have\ns3:PutObject permission on this bucket."},"keyPrefix":{"type":"string","description":"Prefix prepended to each file's name when it is moved to the backup bucket; accepts\nstatic text or handlebars templates. The original directory structure is not preserved —\nonly the filename is appended to this prefix. Ignored unless backupBucket is set."}}},"Wrapper":{"type":"object","description":"Configuration for Wrapper exports, which delegate data retrieval to custom connector code\n(typically a stack-hosted function) rather than a built-in adaptor. Required when the\n_connectionId field references a wrapper connection.","required":["function"],"properties":{"function":{"type":"string","description":"Name of the function the wrapper invokes to retrieve records. Must match a callable\nfunction in the wrapper's execution context; names are case-sensitive."},"configuration":{"oneOf":[{"type":"object","additionalProperties":true},{"type":"array","maxItems":0}],"description":"Free-form settings passed to the wrapper function at runtime (connector-specific keys such\nas method, apiVersion, headers, relativePath, or body). Structure is defined by the wrapper\ncode, not by this schema. May be stored as an empty array when no settings exist."}}},"Parsers":{"type":"array","description":"Configuration for parsing XML payloads (i.e. files, HTTP responses, etc.). Use this field when you need to process XML data\nand transform it into a JSON records.\n\n**Implementation notes**\n\n- This is where you configure how to parse XML data in your resource\n- Although defined as an array, you typically only need a single parser configuration\n- Currently only XML parsing is supported\n- Only configure this field when working with XML data that needs structured parsing\n","items":{"type":"object","properties":{"version":{"type":"string","description":"Version identifier for the parser configuration format. Currently only version \"1\" is supported.\n\nAlways set this field to \"1\" as it's the only supported version at this time.\n","enum":["1"]},"type":{"type":"string","description":"Defines the type of parser to use. Currently only \"xml\" is supported.\n\nWhile the system is designed to potentially support multiple parser types in the future,\nat this time only XML parsing is implemented, so this field must be set to \"xml\".\n","enum":["xml"]},"name":{"type":"string","description":"Optional identifier for the parser configuration. This field is primarily for documentation\npurposes and is not functionally used by the system.\n\nThis field can be omitted in most cases as it's not required for parser functionality.\n"},"rules":{"type":"object","description":"Configuration rules that determine how XML data is parsed and converted to JSON.\nThese settings control the structure and format of the resulting JSON records.\n\n**Parsing options**\n\nThere are two main parsing strategies available:\n- **Automatic parsing**: Simple but produces more complex output\n- **Custom parsing**: More control over the resulting JSON structure\n","properties":{"V0_json":{"type":"boolean","description":"Controls the XML parsing strategy.\n\n- When set to **true** (Automatic): XML data is automatically converted to JSON without\n  additional configuration. This is simpler to set up but typically produces more complex\n  and deeply nested JSON that may be harder to work with.\n\n- When set to **false** (Custom): Gives you more control over how the XML is converted to JSON.\n  This requires additional configuration (like listNodes) but produces cleaner, more\n  predictable JSON output.\n\nMost implementations use the Custom approach (false) for better control over the output format.\n"},"listNodes":{"type":"array","description":"Paths of elements that always parse as arrays. An element that can repeat parses as an\nobject when it occurs once and as an array when it occurs more than once, so list every\nrepeating element here to give each record the same shape regardless of item count.\nEach entry is the element's full path from the record element selected by the resource\npath (from the document root when there is no resource path), with an optional leading\nslash. Only relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"includeNodes":{"type":"array","description":"Limits which XML nodes are included in the output JSON.\n\nFor large XML documents, you can use this field to extract only the nodes you need,\nreducing the size and complexity of the resulting JSON. Only nodes specified here\n(and their children) will be included in the output.\n\nEach entry should be a simplified XPath expression pointing to nodes to include.\n\nOnly relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"excludeNodes":{"type":"array","description":"Specifies which XML nodes should be excluded from the output JSON.\n\nSometimes it's easier to specify which nodes to exclude rather than which to include.\nUse this field to identify nodes that should be omitted from the output JSON.\n\nEach entry should be a simplified XPath expression pointing to nodes to exclude.\n\nOnly relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"stripNewLineChars":{"type":"boolean","description":"Controls whether newline characters are removed from text values.\n\nWhen set to true, all newline characters (\\n, \\r, etc.) will be removed from\ntext content in the XML before conversion to JSON.\n","default":false},"trimSpaces":{"type":"boolean","description":"Controls whether leading and trailing whitespace is trimmed from text values.\n\nWhen set to true, all values will have leading and trailing whitespace removed\nbefore conversion to JSON.\n","default":false},"attributePrefix":{"type":"string","description":"Specifies a character sequence to prepend to XML attribute names when converted to JSON properties.\n\nIn XML, both elements and attributes can exist at the same level, but in JSON this distinction is lost.\nTo maintain the distinction between element data and attribute data in the resulting JSON, this prefix\nis added to attribute names during conversion.\n\nFor example, with attributePrefix set to \"Att-\" and an XML element like:\n```xml\n<product id=\"123\">Laptop</product>\n```\n\nThe resulting JSON would be:\n```json\n{\n  \"product\": \"Laptop\",\n  \"Att-id\": \"123\"\n}\n```\n\nThis helps maintain the distinction between element content and attribute values in the\nconverted JSON, making it easier to reference specific data in downstream processing steps.\n"},"textNodeName":{"type":"string","description":"Specifies the property name to use for element text content when an element has both\ntext content and child elements or attributes.\n\nWhen an XML element contains both text content and other nested elements or attributes,\nthis field determines what property name will hold the text content in the resulting JSON.\n\nFor example, with textNodeName set to \"value\" and an XML element like:\n```xml\n<item id=\"123\">\n  Laptop\n  <category>Electronics</category>\n</item>\n```\n\nThe resulting JSON would be:\n```json\n{\n  \"item\": {\n    \"value\": \"Laptop\",\n    \"category\": \"Electronics\",\n    \"id\": \"123\"\n  }\n}\n```\n\nThis allows for unambiguous parsing of complex XML structures that mix text content with\nchild elements. Choose a name that's unlikely to conflict with actual element names in your XML.\n"}}}}}},"Mappings":{"type":"array","description":"Array of field mapping configurations for transforming data from one format into another.\n\n**Guidance**\n\nThis schema is designed around RECURSION as its core architectural principle. Understanding this recursive\nnature is essential for building effective mappings:\n\n1. The schema is self-referential by design - a mapping can contain nested mappings of the same structure\n2. Complex data structures (nested objects and arrays of objects, to any depth) are ALL\n   handled through this recursive pattern; an array of arrays is not (build one in a script hook)\n3. Each mapping handles one level of the data structure; deeper levels are handled by nested mappings\n\nWhen generating mappings programmatically:\n- For simple fields (string, number, boolean): Create single mapping objects\n- For objects: Create a parent mapping with nested 'mappings' array containing child field mappings\n- For arrays: Use 'buildArrayHelper' with extract paths defining array inputs and\n  recursive 'mappings' to define object structures\n\nThe system will process these nested structures recursively during runtime, ensuring proper construction\nof complex hierarchical data while maintaining excellent performance.\n\nOn save, a leaf row with neither a source (`extract`, `hardCodedValue`, `lookupName`) nor a\ndestination (`generate`) is dropped silently, at every nesting level, and so is an object or\nobject-array row left with no source, no destination and no remaining child `mappings` or\n`buildArrayHelper` entries; a row with only one of the two, and a source-less, destination-less\nwrapper that still has children, are kept.\n\nThe shape of the array is the mapper's **JSON structure**. Named top-level items produce an object\nrecord (`record { }`). Exactly one top-level item with `dataType: objectarray` and no `generate` is\nthe `rows [ ]` structure: the mapped record is an array — usually one element per element of the\nsource array that root's `buildArrayHelper` iterates — Handlebars templates such as `http.body`\nbind it as `rows` instead of `record`, and the mapping editor shows that row's destination disabled.\n","items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}}},"items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}},"Transform":{"type":"object","description":"Configuration for transforming data during processing operations. This object enables\nreshaping of records.\n\n**Transformation capabilities**\n\nCeligo's transformation engine offers powerful features for data manipulation:\n- Precise field mapping with JSONPath expressions\n- Support for any level of nested arrays\n- Formula-based field value generation\n- Dynamic references to flow and integration settings\n\n**Implementation approaches**\n\nThere are two distinct transformation mechanisms available:\n\n**Rule-Based Transformation (`type: \"expression\"`)**\n- **Best For**: Most transformation scenarios from simple to complex\n- **Capabilities**: Field mapping, formula calculations, lookups, nested data handling\n- **Advantages**: Visual configuration, no coding required, intuitive interface\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear mapping requirements or need to reshape data structure\n\n**Script-Based Transformation (`type: \"script\"`)**\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Capabilities**: Full programmatic control, custom processing, complex business rules\n- **Advantages**: Maximum flexibility, can implement any transformation logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Visual transformation tools aren't sufficient for your use case\n","properties":{"type":{"type":"string","description":"Determines which transformation mechanism to use. This choice affects which properties\nmust be configured and how transformation logic is implemented.\n\n**Available types**\n\n**Rule-Based Transformation (`\"expression\"`)**\n- **Required Config**: The `expression` object with mapping definitions\n- **Behavior**: Applies declarative rules to reshape data\n- **Best For**: Most transformation scenarios from simple to complex\n- **Advantages**: Visual configuration, no coding required\n\n**Script-Based Transformation (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to transform data\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard data transformations, use `\"expression\"`\n2. For complex logic or specialized processing, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based transformations. This object enables reshaping data\nwithout requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define transformation rules that\ncan map, modify, and generate data elements.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Field mappings define how input data is transformed to target fields\n- Formulas can be used to calculate or generate new values\n- Lookups can enrich data by fetching related information\n- Mode determines how records are processed (create new or modify existing)\n","properties":{"version":{"type":"string","description":"Version of the expression format. Determines which rules\nproperty contains the transformation logic.\n","enum":["1","2"]},"rules":{"type":"array","description":"Transformation rules for version 1 expressions. An array of\nrule groups; each group is an array of field-mapping objects.\nMost transforms have a single group. Present when `version`\nis `\"1\"`. The output record contains ONLY the generated\nfields — every unmapped field is dropped (v1 has no\nequivalent of Transform 2.0's `modify` mode), and the\nrecord's trace key does not survive the rebuild.\n","items":{"type":"array","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path to read from. Supports multiple\nsyntaxes: bare field names (`id`), dot notation\n(`fulfillment.shipment_id`), slash-prefixed paths\nfor XML (`/FeedProcessingStatus`), wildcards (`*.id`,\n`*.[Internal ID]`), and array indexing (`SDF[0]`).\n"},"generate":{"type":"string","description":"Target field name to write to. Typically a bare name\n(`id`) or dot path (`SDF.Filter.ID`).\n"},"key":{"type":"string","description":"Auto-generated identifier for this rule, used by the\nUI to track individual rules for editing and reordering.\n"}},"required":["extract","generate"]}}},"rulesTwoDotZero":{"type":"object","description":"Configuration for version 2 transformation rules. This object contains the core logic\nfor how data is mapped, enriched, and transformed.\n\n**Capabilities**\n\nTransformation 2.0 provides:\n- Precise field mapping with JSONPath expressions\n- Support for deeply nested data structures\n- Formula-based field generation\n- Dynamic lookups for data enrichment\n- Multiple operating modes to fit different scenarios\n","properties":{"mode":{"type":"string","description":"Transformation mode that determines how records are handled during processing.\n\n**Available modes**\n\n**Create Mode (`\"create\"`)**\n- **Behavior**: Builds entirely new output records from inputs\n- **Use When**: Output structure differs significantly from input\n- **Advantage**: Clean slate approach, no field inheritance\n\n**Modify Mode (`\"modify\"`)**\n- **Behavior**: Makes targeted edits to existing records\n- **Use When**: Output structure should remain similar to input\n- **Advantage**: Preserves unmapped fields from the original record\n","enum":["create","modify"]},"mappings":{"$ref":"#/components/schemas/Mappings"},"lookups":{"allOf":[{"description":"Shared lookup tables used across all mappings defined in the transformation rules.\n\n**Purpose**\n\nLookups provide centralized value translation that can be referenced from any mapping\nin your transformation configuration. They enable consistent translation of codes, IDs,\nand values between systems without duplicating translation logic.\n\n**Usage in transformations**\n\nLookups are particularly valuable in transformations for:\n\n- **Data Normalization**: Standardizing values from diverse source systems\n- **Code Translation**: Converting between different coding systems (e.g., status codes)\n- **Field Enrichment**: Adding descriptive values based on ID or code lookups\n- **Cross-Reference Resolution**: Mapping identifiers between integrated systems\n\n**Implementation**\n\nLookups are defined once in this array and referenced by name in mappings:\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"statusMapping\",\n    \"map\": {\n      \"A\": \"Active\",\n      \"I\": \"Inactive\",\n      \"P\": \"Pending\"\n    },\n    \"default\": \"Unknown Status\"\n  }\n]\n```\n\nThen referenced in mappings using the lookupName property:\n\n```json\n{\n  \"generate\": \"status\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.statusCode\",\n  \"lookupName\": \"statusMapping\"\n}\n```\n\nThe system automatically applies the lookup during transformation processing.\n\nFor complete details on lookup properties and behavior, see the Lookups schema.\n"},{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the JSON shape the transformTwoDotZero processor\nevaluates `mappings[].extract` JSONPath values against at\nflow runtime. Applies only to Transform 2.0 (v2,\n`rulesTwoDotZero`); v1 transforms (the `rules` array on\n`transform.expression.rules`) and script-mode transforms\nignore this field.\n"}}}}},"script":{"type":"object","description":"Configuration for programmable script-based transformations. This object enables complex, custom\ntransformation logic beyond what expression-based transformations can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to transform data according to\nspecialized business rules or complex algorithms.\n\n**Implementation approach**\n\nScript-based transformation works by:\n1. Executing the specified function from the referenced script\n2. Passing input data to the function\n3. Using the function's return value as the transformed output\n\n**Common use cases**\n\nScript transformation is ideal for:\n- Complex business logic that can't be expressed through mappings\n- Algorithmic transformations requiring computation\n- Dynamic transformations based on external factors\n- Legacy system data format compatibility\n- Multi-stage processing with intermediate steps\n\nOnly use script-based transformation when expression-based transformation is insufficient.\nScript transformation requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to a predefined script resource containing the transformation logic.\n\nThe referenced script should contain the function specified in the\n'function' property. It must be a script in this account: any other id fails the save\nwith 422 (\"Only a script owned by the user or a script shared by the Integration App can\nbe used at transform.script._scriptId.\").\n","format":"objectid"},"function":{"type":"string","description":"Name of the function within the script to execute for transformation. This function\nmust exist in the script referenced by _scriptId.\n"}}}}},"Lookups":{"type":"array","description":"Configuration for value-to-value transformations using lookup tables.\n\n**Purpose**\n\nLookups provide a way to translate values from one system to another. They transform\ninput values into output values using either static mapping tables or\ndynamic lookup caches.\n\n**Lookup mechanisms**\n\nThere are two distinct lookup mechanisms available:\n\n1. **Static Lookups**: Define a simple key-value map object and store it as part of your resource\n   - Best for: Small, fixed sets of values that rarely change\n   - Implementation: Configure the `map` object with input-to-output value mappings\n   - Example: Country codes, status values, simple translations\n\n2. **Dynamic Lookups**: Reference an existing 'Lookup Cache' resource in your Celigo account\n   - Best for: Large datasets, frequently changing values, or complex reference data\n   - Implementation: Configure `_lookupCacheId` to reference cached data maintained independently\n   - Example: Product catalogs, customer databases, pricing information\n\n**Property usage**\n\nThere are two mutually exclusive ways to configure lookups, depending on which mechanism you choose:\n\n1. **For Static Mappings**: Configure the `map` property with a direct key-value object\n   ```json\n   \"map\": {\"US\": \"United States\", \"CA\": \"Canada\"}\n   ```\n\n2. **For Dynamic Lookups**: Configure the following properties:\n   - `_lookupCacheId`: Reference to the lookup cache resource\n   - `extract`: JSON path to extract specific value from the returned lookup object\n\n**When to use**\n\nLookups are ideal for:\n\n1. **Value Translation**: Mapping codes or IDs to human-readable values\n\n2. **Data Enrichment**: Adding related information to records during processing\n\n3. **Normalization**: Ensuring consistent formatting of values across systems\n\n**Implementation details**\n\nLookups can be referenced in:\n\n1. **Field Mappings**: Direct use in field transformation configurations\n\n2. **Handlebars Templates**: Use within templates with the syntax:\n   ```\n   {{lookup 'lookupName' record.fieldName}}\n   ```\n\n**Example usage**\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"countryCodeToName\",\n    \"map\": {\n      \"US\": \"United States\",\n      \"CA\": \"Canada\",\n      \"UK\": \"United Kingdom\"\n    },\n    \"default\": \"Unknown Country\",\n    \"allowFailures\": true\n  },\n  {\n    \"name\": \"productDetails\",\n    \"_lookupCacheId\": \"60a2c4e6f321d800129a1a3c\",\n    \"extract\": \"$.details.price\",\n    \"allowFailures\": false\n  }\n]\n```\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for the lookup table within this configuration.\n\nThis name must be unique within the scope where the lookup is defined and is used to reference\nthe lookup in handlebars templates with the syntax {{lookup 'name' value}}.\n\nChoose descriptive names that indicate the transformation purpose, such as:\n- \"countryCodeToName\" for country code to full name conversion\n- \"statusMapping\" for status code translations\n- \"departmentCodes\" for department code to name mapping\n"},"map":{"type":["object","null"],"description":"The lookup mapping table as key-value pairs. The platform stores `null`\nhere on dynamic lookups, which resolve values at runtime instead of\nfrom a static table.\n\nThis object contains the input values as keys and their corresponding\noutput values. When a input value matches a key in this object,\nit will be replaced with the corresponding value.\n\nThe map should be kept to a reasonable size (typically under 100 entries)\nfor optimal performance. For larger mapping requirements, consider using\ndynamic lookups instead.\n\nMaps can include:\n- Simple code to name conversions: {\"US\": \"United States\"}\n- Status transformations: {\"A\": \"Active\", \"I\": \"Inactive\"}\n- ID to name mappings: {\"100\": \"Marketing\", \"200\": \"Sales\"}\n\nValues can be strings, numbers, or booleans, but all are stored as strings\nin the configuration.\n"},"_lookupCacheId":{"type":"string","description":"Reference to a LookupCache resource that contains the reference data for the lookup.\n\n**Purpose**\n\nThis field connects the lookup to an external data source that has been cached in the system.\nUnlike static lookups that use the `map` property, dynamic lookups can reference large datasets\nor frequently changing information without requiring constant updates to the integration.\n\n**Implementation details**\n\nThe LookupCache resource referenced by this ID contains:\n- The data records to be used as a reference source\n- Configuration for how the data should be indexed and accessed\n- Caching parameters to balance performance with data freshness\n\n**Usage patterns**\n\nCommonly used to reference:\n- Product catalogs or SKU databases\n- Customer or account information\n- Pricing tables or discount rules\n- Complex business logic lookup tables\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n","format":"objectid"},"extract":{"type":"string","description":"JSON path expression that extracts a specific value from the cached lookup object.\n\n**Purpose**\n\nWhen using dynamic lookups with a LookupCache, this JSON path identifies which field to extract\nfrom the cached object after it has been retrieved using the lookup key.\n\n**Implementation details**\n\n- Must use JSON path syntax (similar to mapping extract fields)\n- Operates on the cached object returned by the lookup operation\n- Examples:\n  - \"$.name\" - Extract the name field from the top level\n  - \"$.details.price\" - Extract a nested price field\n  - \"$.attributes[0].value\" - Extract a value from the first element of an array\n\n**Usage scenario**\n\nWhen a lookup cache contains complex objects:\n```json\n// Cache entry for key \"PROD-123\":\n{\n  \"id\": \"PROD-123\",\n  \"name\": \"Premium Widget\",\n  \"details\": {\n    \"price\": 99.99,\n    \"currency\": \"USD\",\n    \"inStock\": true\n  }\n}\n```\n\nSetting extract to \"$.details.price\" would return 99.99 as the lookup result.\n\nIf no extract is provided, the entire cached object is returned as the lookup result.\n"},"default":{"type":["string","null"],"description":"Default value to use when the source value is not found in the lookup map.\nThe platform stores `null` here when no default is configured.\n\nThis value is used as a fallback when:\n1. The source value doesn't match any key in the map\n2. allowFailures is true or omitted\n\nSetting an appropriate default helps prevent flow failures due to unexpected\nvalues and provides predictable behavior for edge cases.\n\nCommon default patterns include:\n- Descriptive unknowns: \"Unknown Country\", \"Unspecified Status\"\n- Original value indicators: \"{Original Value}\", \"No mapping found\"\n- Neutral values: \"Other\", \"N/A\", \"Miscellaneous\"\n\nA save refuses a default together with an explicit `allowFailures: false`\n(422 `Lookups with default set can not have allowFailures explicitly set to\nfalse.`): a lookup either fails a miss (`allowFailures: false`, no default) or\nfalls back to its default.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, missing lookup values will use the default value rather than causing an error.\n\n**Behavior control**\n\nThis field determines how the system handles source values that don't exist in the map:\n\n- true: Use the default value for missing mappings and continue processing\n- false: Treat missing mappings as errors, failing the record (`lookup_error` for a\n  static map, `lookupcache_evaluation_failed` for a lookup cache)\n- omitted or null: the record doesn't fail either. On an import a static lookup writes\n  its default if it has one, and a static or cache lookup without one leaves the\n  mapped field out of the output, with no error\n\n**Recommendation**\n\nSet this to true when:\n- New source values might appear over time\n- Data quality issues could introduce unexpected values\n- Processing should continue even with imperfect mapping\n\nSet this to false when:\n- Complete data accuracy is critical\n- All possible source values are known and controlled\n- Missing mappings indicate serious data problems that should be addressed\n\nA default raises no error: records that miss go out with the default value and\nnothing reports them. Use true only when that fallback is acceptable at the\ndestination, and set false explicitly when a missing value must stop the record.\n"}}}},"Form":{"type":"object","description":"Configuration for creating user-friendly settings forms that make it easier for less technical users\nto configure integration resources.\n\n**Settings form builder**\n\nThe Settings Form Builder allows you to create or edit user-friendly fields that prompt for text entry\nor selections that will be returned as settings applied to this resource. Your forms can include any\nfield types that you see elsewhere in integrator.io, such as:\n\n- Text fields\n- Dropdown selections\n- Checkboxes\n- Radio buttons\n- Date pickers\n- Multi-select fields\n- Search fields\n\nForm fields make it much easier for less technical users to work with your integration settings by:\n\n- Providing clear labels and help text\n- Enforcing validation rules\n- Offering pre-defined selection options\n- Grouping related settings logically\n- Supporting conditional visibility\n- Creating a consistent user experience\n","properties":{"form":{"type":"object","description":"Configuration that defines the structure, fields, and behavior of the settings form.\n\nThis object contains the complete definition of the form's layout, fields, validation rules,\nand interactive behaviors. The specific structure depends on the form complexity and can include\nfield definitions, sections, conditional display logic, and default values.\n\nThe form configuration is typically created and managed through the visual Form Builder interface\nrather than edited directly as JSON.\n","properties":{"fieldMap":{"type":"object","description":"A mapping of field identifiers to their configuration objects.\nEach key in this object represents a unique field ID, and the value contains\nall the configuration settings for that specific form field.\n","additionalProperties":{"type":"object","description":"Configuration for an individual form field.\n","properties":{"id":{"type":"string","description":"Unique identifier for this field within the form: layout containers and conditions\nrefer to the field by it. Use the same string as the field's fieldMap key and its `name`.\n"},"name":{"type":"string","description":"Key the field's value is saved under: one top-level key of the resource's `settings`,\nwhich the form also reads the value back from when it opens. Nested objects inside\n`settings` are written to it directly, not through a form field's `name`.\n"},"type":{"type":"string","description":"The type of form control to render for this field.\n","enum":["text","checkbox","radiogroup","relativeuri","editor","keyvalue","select","multiselect","toggle","datetime","date","exportSelect","staticMap"]},"label":{"type":"string","description":"Display label shown next to the field in the form.\n"},"description":{"type":"string","description":"Detailed explanation text that appears below the field, providing more context\nthan the label or helpText.\n"},"helpText":{"type":"string","description":"Explanatory text that appears when hovering over the help icon next to the field.\nUsed to provide additional guidance on how to use the field.\n"},"required":{"type":"boolean","description":"When true, the field must have a value before the form can be submitted. Only the form\nenforces it: an API write of `settings` is stored without the value.\n","default":false},"defaultValue":{"description":"Value the form shows while the resource's `settings` holds no value under the field's\n`name`.\n"},"multiline":{"type":"boolean","description":"For text fields, determines whether the input should be a multi-line text area\ninstead of a single-line input.\n","default":false},"rowsMax":{"type":"integer","description":"For multiline text fields, specifies the maximum number of visible rows.\n"},"inputType":{"type":"string","description":"For text fields, specifies the HTML input type attribute to apply additional\nvalidation or specialized input behavior.\n","enum":["text","number","email","password","tel","url"]},"delimiter":{"type":"string","description":"For text fields, specifies a character to use for splitting the input into an array.\nUsed for collecting multiple values in a single text field.\n"},"mode":{"type":"string","description":"For editor fields, specifies the type of content being edited for syntax highlighting.\n","enum":["json","xml","csv","text"]},"keyName":{"type":"string","description":"For keyvalue and staticMap fields, names the first column: each saved row holds that\ncolumn's value under this key. A keyvalue field without it uses `key`.\n"},"valueName":{"type":"string","description":"For keyvalue and staticMap fields, names the second column: each saved row holds that\ncolumn's value under this key. A keyvalue field without it uses `value`.\n"},"keyLabel":{"type":"string","description":"For staticMap fields, heading of the first column.\n"},"valueLabel":{"type":"string","description":"For staticMap fields, heading of the second column.\n"},"keyOptions":{"type":"array","description":"For staticMap fields, suggestions offered in the first column's cells. A cell still\naccepts a typed value.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"Display text for the suggestion.\n"},"value":{"type":"string","description":"Value saved when the suggestion is chosen.\n"}}}},"valueOptions":{"type":"array","description":"For staticMap fields, suggestions offered in the second column's cells. A cell still\naccepts a typed value.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"Display text for the suggestion.\n"},"value":{"type":"string","description":"Value saved when the suggestion is chosen.\n"}}}},"keyResource":{"type":"object","description":"For staticMap fields, fills the first column's suggestions from an export the form\ncarries inline in `virtual`, run the way an exportSelect's `resource` is. With\n`keyResource` or `valueResource` set, the form requires both columns in every row\nunless `keyValueRequired` is false.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline, shaped like an exportSelect's `resource.virtual`.\n","additionalProperties":true}}},"valueResource":{"type":"object","description":"For staticMap fields, fills the second column's suggestions from an export the form\ncarries inline in `virtual`, run the way an exportSelect's `resource` is.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline, shaped like an exportSelect's `resource.virtual`.\n","additionalProperties":true}}},"keyValueRequired":{"type":"boolean","description":"For staticMap fields with `keyResource` or `valueResource`, when false, saves rows\nthat fill only one column; by default the form drops them.\n","default":true},"resource":{"type":"object","description":"For exportSelect fields, the export whose records become the options. When the form\nopens, the UI previews the export in `virtual` (with `test.limit` 100 unless the export\nsets its own) and lists the preview's `data`: each record needs a `label` and a `value`\n(a string or number serves as both), so shape them with the export's `transform`. A\nsaved export can't be referenced, and an HTTP export that finds no records leaves the\nfield showing an error instead of options.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline: `adaptorType`, its adaptor block, and an optional\n`transform`. `_connectionId` defaults to the connection of the export or import the\nform belongs to; set it on any other resource's form.\n","additionalProperties":true}}},"multiselect":{"type":"boolean","description":"For exportSelect fields, when true, the user can choose several options and the value\nis saved as an array.\n","default":false},"supportHandlebars":{"type":"boolean","description":"For exportSelect fields, when true, renders the `resource.virtual` export as a Handlebars\ntemplate before each preview (all but its `transform`, `hooks`, and `filter`). On an\nintegration, flow group, flow, export, or import form, the form's current values are\nunder that form's scope name (`integration`, `flowGrouping`, `flow`, `export`, or\n`import`), beside the saved settings of the scopes above it, with no `settings.`\nprefix; on a connection or iClient form the template has no context. The options then\nload through `refreshOptionsOnChangesTo` or `typeAheadSearch`, or when the user\nrefreshes the field.\n","default":false},"refreshOptionsOnChangesTo":{"type":"array","items":{"type":"string"},"description":"For exportSelect fields with `supportHandlebars`, ids of the fields whose values the\ntemplated export uses: the options load once one of them has a value and reload whenever\none changes.\n"},"typeAheadSearch":{"type":"boolean","description":"For exportSelect fields, when true, re-runs the templated export as the user types, with\nthe typed text as this field's own value; an empty box clears the options, and typed\ntext is accepted as the value.\n","default":false},"showDelete":{"type":"boolean","description":"For keyvalue fields, determines whether to show a delete button for each key-value pair.\n"},"doNotAllowFutureDates":{"type":"boolean","description":"For date and datetime fields, restricts selection to dates not in the future.\n"},"skipTimezoneConversion":{"type":"boolean","description":"For datetime fields, prevents automatic timezone conversion of the date value.\n"},"options":{"type":"array","description":"For fields that present choices (select, multiselect, radiogroup, toggle), defines\nthe available options.\n","items":{"anyOf":[{"title":"Option group","type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"oneOf":[{"title":"String value","type":"string"},{"title":"Label-value pair","type":"object","properties":{"label":{"type":"string","description":"Display text for the option.\n"},"value":{"type":"string","description":"Value to store when this option is selected.\n"}}}]},"description":"Array of option values/labels to display in the selection control.\n"}}},{"title":"Label-value pair","type":"object","required":["label"],"properties":{"label":{"type":"string","description":"Display text for the option.\n"},"value":{"type":"string","description":"Value to store when this option is selected.\n"}}}]}},"visibleWhen":{"type":"array","description":"Conditional display rules that determine when this field should be visible.\nIf empty or not provided, the field is always visible.\n","items":{"type":"object","properties":{"field":{"type":"string","description":"The ID of another field whose value controls the visibility of this field. The\nsave doesn't check it: a rule naming an ID that isn't in the form counts as met,\nso the field stays visible.\n"},"is":{"type":"array","items":{"type":["string","boolean","number","null"]},"description":"Array of values - if the referenced field has any of these values,\nthis field will be visible. Values may be strings, booleans (for\ncheckbox/toggle fields), numbers (for numeric inputs), or null.\n"}}}}}}},"layout":{"type":"object","description":"Defines how the form fields are arranged and grouped in the UI.\nThe layout can organize fields into columns, sections, or other visual groupings.\n","properties":{"type":{"type":"string","description":"The type of layout to use for the form.\n","enum":["column","collapse","box","indent","tabWithoutSave","verticalTabWithoutSave"]},"fields":{"type":"array","items":{"type":"string"},"description":"Field IDs shown above the containers, without a heading.\n"},"containers":{"type":"array","description":"Array of container objects that group fields or contain nested containers.\nEach container can represent a column, box, indented section, or collapsible section.\n","items":{"type":"object","properties":{"type":{"type":"string","description":"The visual style of the container.\n","enum":["indent","box","collapse"]},"label":{"type":"string","description":"The heading text displayed for this container.\n"},"fields":{"type":"array","items":{"type":"string"},"description":"Array of field IDs that should be displayed in this container.\nEach ID must correspond to a key in the fieldMap object: the save doesn't check it,\nand an ID that doesn't renders nothing. A field the layout doesn't list isn't shown.\n"},"containers":{"type":"array","description":"Nested containers within this container. Allows for hierarchical organization\nof fields with different visual styles.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"The heading text displayed for this nested container.\n"},"fields":{"type":"array","items":{"type":"string"},"description":"Array of field IDs that should be displayed in this nested container.\n"}}}}}}}}}},"additionalProperties":true},"init":{"type":"object","description":"Configuration for custom JavaScript initialization that executes when the form is first loaded.\n\nThis object defines a JavaScript hook that prepares the form for use, sets initial field values,\nperforms validation, or otherwise customizes the form behavior before it is displayed to the user.\n\n**Function signature**\n\nThe initialization function is invoked with a single 'options' argument containing contextual information:\n```javascript\nfunction formInit(options) {\n  // Process options and return the form object\n  return options.resource.settingsForm.form;\n}\n```\n\n**Available context**\n\nThe 'options' argument provides access to:\n- `options.resource` - The current resource being configured\n- `options.parentResource` - The parent of the current resource\n- `options.grandparentResource` - The grandparent of the current resource\n- `options.license` - For integration apps, the license provisioned to the integration\n- `options.parentLicense` - For integration apps, the parent of the license\n- `options.sandbox` - Boolean flag indicating whether the script is running in a sandbox environment\n\n\n**Common uses**\n\n- Dynamically generate field options based on resource configuration\n- Pre-populate default values from related resources\n- Apply conditional logic that depends on resource properties\n- Add, remove, or modify form fields based on user permissions or account settings\n- Fetch external data to populate selection options\n- Implement complex validation rules that depend on resource context\n- Create branching form experiences based on user selections\n\n**Return value**\n\nThe function must return a valid form object that the UI can render.\nThrowing an exception will signal an error to the user.\n\nThe UI runs the function each time it opens the form and renders the returned form instead of\n`form`, then fills in the values from `settings`; a thrown error's message replaces the form.\n","properties":{"function":{"type":"string","description":"The name of the function to execute within the referenced script.\n\nThis property specifies which function to invoke from the script\nreferenced by _scriptId. The function will be called when the form\nis initialized and should handle any custom setup logic.\n\nThe function must follow the expected signature and return a valid form object.\n"},"_scriptId":{"type":"string","format":"objectId","description":"Reference to a predefined script resource containing the initialization function.\n\nThe referenced script should contain the function specified in the\n'function' property. On an integration, a flow group, a flow, an export, an import, or a\nconnection, an id that isn't a script in the same account (or, on an Integration App's\nresources, one the app shares) is refused with 422 `invalid_ref`. An iClient save\nstores the id without that check.\n"}}}}},"Settings":{"type":"object","description":"Configuration settings that can be accessed by hooks, filters, mappings and handlebars templates at runtime.\n\nIt enables customization of the resource's logic, allowing hooks, mappings, filters, and\nhandlebars to access and apply the settings at runtime.\n\n**Usage**\n\nThe settings object can store arbitrary JSON data that you want to save with the resource.\nWhile it's often populated through a form defined in the `settingsForm` field, you can also:\n\n- Directly provide JSON settings without using a form\n- Store configuration values used by hooks and templates\n- Create resource-specific constants and parameters\n- Maintain lookup tables or mapping structures\n- Define conditional logic parameters\n\n**Accessibility**\n\nSettings are available in:\n- All handlebars fields for building dynamic payloads\n- Field mapping expressions\n- JavaScript hooks via the options object\n- Filters and transformations\n\nAt runtime, the settings objects in the step's execution chain are gathered into a\nsingle `settings` context keyed by fixed scope keys. Flow runs populate the full\nchain: `settings.integration.*`, `settings.flowGrouping.*` (the flow group the flow\nbelongs to), `settings.flow.*`, `settings.connection.*`, `settings.iClient.*`, and\nthe running step's own scope — `settings.export.*` on exports/lookups,\n`settings.import.*` on imports (the key matches the step type). Steps executing\ninside a My API or a Tool receive only the step's own scope plus\n`settings.connection.*` / `settings.iClient.*`; the integration, flowGrouping, and\nflow scopes resolve empty there. The scope key is literal — resource display names\nand settingsForm section labels are never part of the path. The segments after the\nscope mirror the stored settings JSON key path exactly: a top-level field is\n`settings.<scope>.<fieldId>`, and nested objects add one segment per JSON key\n(e.g. `settings.flowGrouping.Customer.region` when the group's settings JSON nests\n`region` under `Customer`). References without a scope key\n(e.g. `{{settings.myField}}`) resolve to empty strings.\n\n**Best practices**\n\nFor non-technical users, create a custom form instead of editing the JSON directly.\nThis provides a user-friendly interface for updating settings without requiring JSON knowledge.\n","additionalProperties":true},"MockOutput":{"type":["object","null"],"description":"Sample data that simulates the output from an export for testing and configuration purposes.\n\nMock output allows you to configure and test flows without executing the actual export or\nwaiting for real-time data to arrive. This is particularly useful for:\n- Initial flow configuration and testing\n- Mapping development without requiring live data\n- Generating metadata for downstream flow steps\n- Creating realistic test scenarios\n- Documenting expected data structures\n\n**Structure**\n\nThe mock output must follow the integrator.io canonical format, which consists of a\n`page_of_records` array containing record objects. Each record object has a `record`\nproperty that contains the actual data fields.\n\n```json\n{\n  \"page_of_records\": [\n    {\n      \"record\": {\n        \"field1\": \"value1\",\n        \"field2\": \"value2\",\n        ...\n      }\n    },\n    ...\n  ]\n}\n```\n\n**Usage**\n\nIn a flow test run, and for an upstream lookup during a page-processor preview\n(`previewPageProcessor`), integrator.io substitutes this mock output for the export's live\nresponse instead of calling the source. A saved mock is not read anywhere else: a target\nlookup in that preview runs its live query, invoking the export\n(`POST /v1/exports/{_id}/invoke`) reads the source, and real runs ignore it. This allows you\nto:\n- Test mappings with representative data\n- Configure downstream flow steps without waiting for real data\n- Simulate various data scenarios\n\nWhere it is substituted, the records stand in for the RAW parsed response: the export's own\n`transform` and output `filter` still run on top of them, so downstream steps — and a\nlookup's results mapping — see the transformed, filtered records, not the shape stored here.\n\nIn a flow test run of an HTTP export with a saved mock (live probe, 2026-10-21), the export\nstill listed `request` and `response` stages: `request` held the request the export composes,\nand `response` held the mock under the source's URL, as did the test run's request log, which\nrecorded it as a 200. Read those stages as the substituted mock, not as a live call.\n\n`POST /v1/exports/preview` reads the `mockOutput` in its request body, not the saved one. For\nan export the preview supports (an HTTP export, for example), a body that carries it (as an\nexport read back with GET does) returns that snapshot as a lone `parse` stage without calling\nthe source; remove `mockOutput` from the body to preview live records.\n\n**Limitations**\n\n- Maximum of 10 records\n- Maximum size of 1 MB\n- Must follow the canonical format shown above\n\nMock output can be populated automatically from preview data (which is the parsed response,\nbefore `transform` / `filter`) or entered manually.\n","properties":{"page_of_records":{"type":"array","description":"Array of record objects in the integrator.io canonical format.\n\nEach item in this array represents one record that would be processed\nby the flow during execution.\n","items":{"type":"object","properties":{"record":{"type":"object","description":"Container for the actual record data fields.\n\nThe structure of this object will vary depending on the specific\nexport configuration and the source system's data structure.\n","additionalProperties":true}}}}}},"PreSave":{"type":"object","description":"Defines a JavaScript hook that executes before the resource is saved.\n\nThis hook allows for programmatic validation, transformation, or enrichment of the\nresource itself before it is persisted. It can be used to enforce business rules,\nset derived properties, or implement cross-field validations that can't be expressed\nthrough the standard UI.\n\n**Function signature**\n\nThe preSave function is invoked with a single 'options' argument containing:\n```javascript\nfunction preSave(options) {\n  // Process options and return the modified resource\n  return options.newResource;\n}\n```\n\n**Available context**\n\nThe 'options' argument provides access to:\n- `options.newResource` - The resource being saved (with pending changes)\n- `options.oldResource` - The previous version of the resource (before changes)\n- `options.sandbox` - Boolean flag indicating whether the script is running in a sandbox environment\n\n\n**Common uses**\n\n- Enforcing complex business rules across multiple fields\n- Automatically deriving field values based on other configuration\n- Performing validation that depends on external systems or data\n- Normalizing or standardizing configuration values\n- Adding computed or derived properties\n- Implementing versioning or change tracking\n- Dynamically looking up data using the Celigo API module to enrich configuration\n\n**Return value**\n\nThe function must return the newResource object (potentially modified) to be saved.\nThrowing an exception will prevent saving and signal an error to the user.\n","properties":{"function":{"type":"string","description":"The name of the function to execute within the referenced script.\n\nThis property specifies which function to invoke from the script\nreferenced by _scriptId. The function will be called just before\nthe resource is saved.\n\nThe function must follow the expected signature and return the resource object.\n"},"_scriptId":{"type":"string","format":"objectId","description":"Reference to a predefined script resource containing the preSave function.\n\nThe referenced script should contain the function specified in the\n'function' property. This script must be accessible within the user's account\nand have appropriate permissions.\n"}}},"Export":{"type":"object","required":["_id","name","adaptorType","createdAt","lastModified"],"description":"Export object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ExportBase"},{"$ref":"#/components/schemas/ResourceResponse"},{"$ref":"#/components/schemas/IAResourceResponse"},{"type":"object","properties":{"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"apim":{"$ref":"#/components/schemas/APIM"},"apiIdentifier":{"type":"string","readOnly":true,"description":"API identifier assigned to this export."},"asynchronous":{"type":"boolean","readOnly":true,"description":"Server-managed execution-mode flag set on creation."},"__linkedLookupCacheIds":{"type":"array","readOnly":true,"description":"Lookup caches linked to this export, managed by the platform.","items":{"type":"string","format":"objectId"}},"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the source resource this export was created from."},"_templateId":{"type":"string","format":"objectId","readOnly":true,"description":"Template this export was created from."},"draft":{"type":"boolean","readOnly":true,"description":"When true, this export is in draft state and has not been confirmed."},"draftExpiresAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the draft version of this export expires."},"debugUntil":{"type":"string","format":"date-time","readOnly":true,"description":"Turns on step debug capture until this time: while it lies in the future, every\nrequest this export sends is recorded in the step's debug log\n(`GET /v1/{flows|apis|tools}/{containerId}/{_id}/requests`). Read-only on PUT —\nwritten only by `PATCH /v1/exports/{_id}` on `/debugUntil`, at most 3600 seconds\nahead (`422` `update_not_allowed` beyond that); `remove` stops capture."}}}]},"ResourceResponse":{"type":"object","description":"Response","properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Unique identifier for the resource. Format is a 24-character hexadecimal string."},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was created. Set automatically and cannot be modified."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was last updated. Changes whenever any property is modified."},"deletedAt":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp when the resource was soft-deleted. When null or absent, the resource is active."}},"required":["_id"]},"IAResourceResponse":{"type":"object","description":"Integration app response fields for resources that are part of integration apps","properties":{"_integrationId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the specific integration instance that contains this resource.\n\nThis field is only populated for resources that are part of an integration app\ninstallation. It contains the unique identifier (_id) of the integration\nresource that was installed in the account.\n\nThe integration instance represents a specific installed instance of an\nintegration app, with its own configuration, settings, and runtime environment.\n\nThis reference enables:\n- Tracing the resource back to its parent integration instance\n- Permission and access control based on integration ownership\n- Lifecycle management (enabling/disabling, updating, or uninstalling)\n"},"_connectorId":{"type":"string","format":"objectId","readOnly":true,"description":"Integration App (connector) that defines this resource. Present only on resources an\nIntegration App owns. The app locks parts of these documents, and a PUT that changes a\nlocked field answers 200 but keeps the stored value, so read the document back to confirm\na change."}}},"AIDescription":{"type":"object","description":"AI-generated descriptions and documentation for the resource.\n\nThis object contains automatically generated content that helps users\nunderstand the purpose, behavior, and configuration of the resource without\nrequiring them to analyze the technical details. The AI-generated content\nis sanitized and safe for display in the UI.\n","properties":{"summary":{"type":["string","null"],"description":"Brief AI-generated summary of the resource's purpose and functionality.\n\nThis concise description provides a quick overview of what the resource does,\nwhat systems it interacts with, and its primary role in the integration.\nThe summary is suitable for display in list views, dashboards, and other\ncontexts where space is limited.\n\nMaximum length: 10KB\n"},"detailed":{"type":["string","null"],"description":"Comprehensive AI-generated description of the resource's functionality.\n\nThis detailed explanation covers the resource's purpose, configuration details,\ndata flow patterns, filtering logic, and other technical aspects. It provides\nin-depth information suitable for documentation, tooltips, or detailed views\nin the administration interface.\n\nThe content may include HTML formatting for improved readability.\n\nMaximum length: 10KB\n"},"generatedOn":{"type":["string","null"],"format":"date-time","description":"Timestamp indicating when the AI description was generated.\n\nThis field helps track the freshness of the AI-generated content and\ndetermine when it might need to be regenerated due to changes in the\nresource's configuration or behavior.\n\nThe timestamp is recorded in ISO 8601 format with UTC timezone (Z suffix).\n"}}},"APIM":{"type":"array","description":"Read-only field that stores information about the integration resources\npublished in the API Management (APIM) platform.\n\nThis field tracks the relationship between integrator.io resources and their\npublished counterparts in the APIM platform, which is\ntightly integrated with the Celigo UI. When resources are \"pushed\" to APIM,\nthis field is populated with the relevant identifiers and statuses.\n","items":{"type":"object","properties":{"apiId":{"type":"string","description":"Identifier for the API where this integrator.io resource is published in the APIM.\n\nThis is an APIM resource identifier (not prefixed with underscore like Celigo IDs)\nthat uniquely identifies the API in the API Management platform.\n"},"flowId":{"type":"string","description":"Identifier for the flow within the API where this integrator.io resource is linked.\n\nWhen an API has multiple integrator.io resources linked, each resource is associated\nwith a specific flow in the API, identified by this field. This is an APIM\nresource identifier.\n"},"status":{"type":"string","description":"Indicates the publishing stage of the integrator.io resource in APIM.\n\nPossible values:\n- 'oaspending': The resource is published but the OpenAPI Specification (OAS) is not\n  yet published. The apiId will be updated with the API ID created in APIM.\n- 'published': The OpenAPI Specification for the integrator.io resource has been\n  successfully uploaded to APIM.\n","enum":["oaspending","published"]}}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"400-bad-request":{"description":"Bad request. The server could not understand the request because of malformed syntax or invalid parameters.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/exports/{_id}":{"put":{"summary":"Update an export","description":"Updates an existing export with the provided configuration.\nThis is used for major updates to an export's structure or behavior.\n","operationId":"updateExport","tags":["Exports"],"parameters":[{"name":"_id","in":"path","description":"The unique identifier of the export","required":true,"schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Request"}}}},"responses":{"200":{"description":"Export updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Export"}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
````

## Delete an export

> Deletes an export. The export is soft-deleted and retained in the recycle bin\
> for 30 days before permanent removal. If the export is currently in use by\
> any flows, those flows may fail until reconfigured.<br>

```json
{"openapi":"3.2.0","info":{"title":"Exports","version":"1.0.0"},"tags":[{"name":"Exports","description":"Exports retrieve data from source systems — on a schedule, in delta mode, in real time,\non demand, or as a file/blob transfer — package the results into ≤ 5-MB pages, and pass\neach page to downstream flow steps. Depending on configuration, an export surfaces in the\nFlow Builder as an export, a real-time listener, a file transfer, or a mid-flow lookup.\n\n## Export schema\n\n{% openapi-schemas spec=\"export\" schemas=\"Export\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-dependency-conflict":{"description":"The resource has dependents that must be deleted first. Each entry\nin the `errors` array names one blocking resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}}},"paths":{"/v1/exports/{_id}":{"delete":{"summary":"Delete an export","description":"Deletes an export. The export is soft-deleted and retained in the recycle bin\nfor 30 days before permanent removal. If the export is currently in use by\nany flows, those flows may fail until reconfigured.\n","operationId":"deleteExport","tags":["Exports"],"parameters":[{"name":"_id","in":"path","description":"The unique identifier of the export","required":true,"schema":{"type":"string","format":"objectId"}}],"responses":{"204":{"description":"Export deleted successfully"},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"},"422":{"$ref":"#/components/responses/422-dependency-conflict"}}}}}}
```

## Patch an export

> Partially updates an export using a JSON Patch document (RFC 6902).\
> Only the following whitelisted paths are accepted:\
> \
> \| Path | Operations | Description |\
> \|------|------------|-------------|\
> \| \`/debugUntil\` | \`replace\`, \`add\`, \`remove\` | Step debug-capture expiry (ISO-8601, at most 3600 seconds from now — a later value is rejected with 422 \`update\_not\_allowed\`); \`remove\` stops capture and succeeds even when the field is absent |\
> \| \`/assistantMetadata\` | \`replace\` | Assistant metadata object |\
> \
> All other paths are rejected with \`422\`.

```json
{"openapi":"3.2.0","info":{"title":"Exports","version":"1.0.0"},"tags":[{"name":"Exports","description":"Exports retrieve data from source systems — on a schedule, in delta mode, in real time,\non demand, or as a file/blob transfer — package the results into ≤ 5-MB pages, and pass\neach page to downstream flow steps. Depending on configuration, an export surfaces in the\nFlow Builder as an export, a real-time listener, a file transfer, or a mid-flow lookup.\n\n## Export schema\n\n{% openapi-schemas spec=\"export\" schemas=\"Export\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"JsonPatchRequest":{"type":"array","description":"A JSON Patch document (RFC 6902). Send an array of patch\noperations on whitelisted fields — all other paths are rejected\nwith 422.","minItems":1,"items":{"$ref":"#/components/schemas/JsonPatchOperation"}},"JsonPatchOperation":{"type":"object","description":"A single JSON Patch operation (RFC 6902).","required":["op","path"],"properties":{"op":{"type":"string","enum":["replace","add","remove"],"description":"The operation to perform."},"path":{"type":"string","description":"JSON Pointer (RFC 6901) to the field to patch. Only\nwhitelisted paths are accepted — unlisted paths return\n`422` with `\"<path> is not a whitelisted property\"`."},"value":{"description":"The new value to set. Required for `replace` and `add`, omit for `remove`."}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-unprocessable-entity":{"description":"Unprocessable entity. The request was well-formed but was unable to be followed due to semantic errors.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/exports/{_id}":{"patch":{"summary":"Patch an export","description":"Partially updates an export using a JSON Patch document (RFC 6902).\nOnly the following whitelisted paths are accepted:\n\n| Path | Operations | Description |\n|------|------------|-------------|\n| `/debugUntil` | `replace`, `add`, `remove` | Step debug-capture expiry (ISO-8601, at most 3600 seconds from now — a later value is rejected with 422 `update_not_allowed`); `remove` stops capture and succeeds even when the field is absent |\n| `/assistantMetadata` | `replace` | Assistant metadata object |\n\nAll other paths are rejected with `422`.","operationId":"patchExport","tags":["Exports"],"parameters":[{"name":"_id","in":"path","description":"The unique identifier of the export","required":true,"schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonPatchRequest"}}}},"responses":{"204":{"description":"Export patched successfully"},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
```

## Clone an export

> Creates a copy of an existing export, remapping its connection through \`connectionMap\`.\
> For an export that uses a connection, omitting the map or sending an empty one fails with\
> 400 \`invalid\_connectionMap\`; map the connection to itself to keep it.<br>

````json
{"openapi":"3.2.0","info":{"title":"Exports","version":"1.0.0"},"tags":[{"name":"Exports","description":"Exports retrieve data from source systems — on a schedule, in delta mode, in real time,\non demand, or as a file/blob transfer — package the results into ≤ 5-MB pages, and pass\neach page to downstream flow steps. Depending on configuration, an export surfaces in the\nFlow Builder as an export, a real-time listener, a file transfer, or a mid-flow lookup.\n\n## Export schema\n\n{% openapi-schemas spec=\"export\" schemas=\"Export\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"CloneRequest":{"type":"object","description":"Request body for cloning an export.","properties":{"name":{"type":"string","description":"Optional name for the cloned resource. If omitted, the server may generate a default clone name."},"connectionMap":{"type":"object","description":"Mapping of original connection ids to replacement connection ids.\nKeys are source connection ids on the original resource; values are target connection ids.\nFor an export that uses a connection, omitting the map or sending an empty one fails\nwith 400 `invalid_connectionMap`; map the connection to itself to reuse it in the clone.\n","additionalProperties":{"type":"string"}},"_stackId":{"type":"string","format":"objectId","description":"Stack that every stack reference in the cloned resources (hooks and wrapper\nconnections running on a stack) points at. Required when the clone preview reports\n`stackRequired: true`: without it the clone fails with 400 `stack_not_found`. The\nstack must belong to the target environment or be shared with it (404\n`stack_not_found` for an unknown id, 400 `invalid_stack` otherwise)."}},"additionalProperties":true},"CloneResponse":{"description":"Response body for a clone operation. Some clone endpoints return the cloned resource, while others may return a list of related created resources.","oneOf":[{"$ref":"#/components/schemas/Export"},{"type":"array","items":{"type":"object","properties":{"model":{"type":"string","description":"Model name of the created resource (e.g., Flow, Export, Import)."},"_id":{"type":"string","format":"objectId","description":"Unique id of the created resource."},"name":{"type":"string","description":"Optional name of the created resource."}},"required":["_id"]}}]},"Export":{"type":"object","required":["_id","name","adaptorType","createdAt","lastModified"],"description":"Export object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ExportBase"},{"$ref":"#/components/schemas/ResourceResponse"},{"$ref":"#/components/schemas/IAResourceResponse"},{"type":"object","properties":{"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"apim":{"$ref":"#/components/schemas/APIM"},"apiIdentifier":{"type":"string","readOnly":true,"description":"API identifier assigned to this export."},"asynchronous":{"type":"boolean","readOnly":true,"description":"Server-managed execution-mode flag set on creation."},"__linkedLookupCacheIds":{"type":"array","readOnly":true,"description":"Lookup caches linked to this export, managed by the platform.","items":{"type":"string","format":"objectId"}},"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the source resource this export was created from."},"_templateId":{"type":"string","format":"objectId","readOnly":true,"description":"Template this export was created from."},"draft":{"type":"boolean","readOnly":true,"description":"When true, this export is in draft state and has not been confirmed."},"draftExpiresAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the draft version of this export expires."},"debugUntil":{"type":"string","format":"date-time","readOnly":true,"description":"Turns on step debug capture until this time: while it lies in the future, every\nrequest this export sends is recorded in the step's debug log\n(`GET /v1/{flows|apis|tools}/{containerId}/{_id}/requests`). Read-only on PUT —\nwritten only by `PATCH /v1/exports/{_id}` on `/debugUntil`, at most 3600 seconds\nahead (`422` `update_not_allowed` beyond that); `remove` stops capture."}}}]},"ExportBase":{"type":"object","description":"Writable export fields shared by the request and response schemas.","properties":{"name":{"type":"string","maxLength":100,"description":"Display name for the export, shown in the flow builder, job history, and error logs.\nDescriptive, unique names indicating the data source and purpose make large accounts easier to manage."},"description":{"type":["string","null"],"description":"Free-text summary of what the export retrieves and why. Shown in the UI and available\nto AI agents for context; has no effect on execution.","maxLength":5120},"_connectionId":{"format":"objectId","type":["string","null"],"description":"Connection this export uses to reach the source system. The connection's type must be\ncompatible with the export's `adaptorType` (e.g. an `HTTPExport` needs an `http` connection;\n`NetSuiteExport` and `NetSuiteHTTPExport` both need a `netsuite` connection).\nServer-required unless `type` is `webhook` or `simple` (those receive data instead of\nfetching it) or `adaptorType` is `TopicExport` (Event Streams listeners are delivered\nby the platform itself and must omit it). A `WebhookExport` saved without `type: webhook`\nis refused with 422 \"_connectionId is required unless type is [webhook|simple]\"; a webhook\nexport that does carry a connection saves and updates normally."},"adaptorType":{"type":"string","description":"Selects the adaptor technology that executes this export, which determines the compatible\nconnection types and which adaptor-specific configuration object must also be supplied\n(e.g. set `salesforce` when using `SalesforceExport`).","enum":["HTTPExport","FTPExport","AS2Export","S3Export","NetSuiteExport","NetSuiteHTTPExport","SalesforceExport","JDBCExport","RDBMSExport","MongodbExport","DynamodbExport","WrapperExport","SimpleExport","WebhookExport","FileSystemExport","TopicExport","RESTExport"]},"nsDomainType":{"type":"string","enum":["suitetalk","restlet"],"description":"Selects which NetSuite REST host a `NetSuiteHTTPExport` calls. The server derives the\nfull base URL from the connection's NetSuite account\n(`https://<account>.suitetalk.api.netsuite.com` or `https://<account>.restlets.api.netsuite.com`)\nand signs each request with the connection's token-based credentials, so `http.relativeURI`\ncarries the complete path starting at `/services/rest/...` or `/app/site/hosting/restlet.nl`.\nIgnored on other adaptor types."},"type":{"type":["string","null"],"description":"Operational mode of the export. When omitted, the export retrieves all available records —\nthe standard batch mode, which is also the right choice for parsing structured files\n(CSV/XML/JSON) into records. Most modes take their matching configuration object (e.g. a\n`webhook` object when `type` is `webhook`); an HTTP or SQL `delta` export saves without a\n`delta` object because its request carries `{{lastExportDateTime}}`, though\n`delta.startDate` sets its first window. It still needs `type: delta`: on an export\nwith no `type`, `{{lastExportDateTime}}` and `{{currentExportDateTime}}` render\nempty on every run, so a request that carries them goes out with blank values each time\n(live-verified 2026-10-22). Use `blob` only to transfer raw files without parsing their\ncontents.","enum":["webhook","test","delta","once","tranlinedelta","simple","blob","distributed","stream","all"]},"pageSize":{"type":["integer","null"],"description":"Number of records per page streamed to downstream flow steps when the export is the\nflow's source step. Pages are additionally capped at 5 MB regardless of record count, so\nvery large records may produce smaller pages. The server does not validate the value.\nA lookup (`isLookup: true`) runs to completion and hands every result to the incoming\nrecord as one response, capped at 5 MB in total, so the value does not change how many\nresults a lookup returns; a result set larger than that belongs in a standalone export\nstep. In a test run or preview the value also caps the records fetched (default 20).\nOn a webhook listener the records of each incoming request are paged separately, before\nany hook or downstream step runs.","default":20},"dataURITemplate":{"type":"string","description":"Handlebars template that builds a link back to each record in the source application's UI\n(e.g. `https://my.salesforce.com/lightning/r/Contact/{{record.Id}}/view`). The resolved\nURL is stored with error records in job history so users can jump straight to the record."},"traceKeyTemplate":{"type":["string","null"],"description":"Handlebars template that overrides how each record's unique trace key is generated, used\nto track records through the flow and match retries to prior errors (e.g.\n`{{join \"_\" record.customerId record.orderId}}`). When omitted, the system picks a unique\nfield automatically from the record's top level, by exact field name (`GET\n/v1/flows/{_id}/traceKey` serves the pattern). The rendered value of a template is stamped\non each record as `io_custom_trace_key` and travels downstream with it. If a transform\nreshapes the data first, omit the `record.` prefix. Trace keys are capped at 256 characters;\na longer key is stored truncated from the middle, keeping the beginning and end of the\nvalue. Downstream imports use the trace key as their default concurrency lock key (a blank\nkey means no lock); it never de-duplicates — a record emitted twice with the same key is\nprocessed twice."},"skipRetries":{"type":"boolean","description":"When true, the platform does not retain the source data needed to retry failed records,\nreducing stored data at the cost of being unable to reprocess errors from this export.\nShown as \"Do not store retry data\" in the UI. Defaults to false."},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"isLookup":{"type":"boolean","description":"When true, the export runs as a mid-flow lookup: it executes once per incoming record,\nusing the input record's fields to parameterize the request, and passes the results to\nsubsequent steps. When false, the export runs as a standalone data source. A lookup's\nresults are not paged through the flow: every page the adaptor fetches is merged into one\nresponse for the record, up to 5 MB, so `pageSize` does not limit or extend what a lookup\nreturns. A lookup that must return more than that is better modeled as a standalone\nexport step."},"groupByFields":{"$ref":"#/components/schemas/GroupBy"},"delta":{"$ref":"#/components/schemas/Delta"},"test":{"$ref":"#/components/schemas/Test"},"once":{"$ref":"#/components/schemas/Once"},"webhook":{"$ref":"#/components/schemas/Webhook"},"simple":{"$ref":"#/components/schemas/Simple"},"distributed":{"$ref":"#/components/schemas/Distributed"},"cdc":{"$ref":"#/components/schemas/Cdc"},"topic":{"$ref":"#/components/schemas/Topic"},"filesystem":{"$ref":"#/components/schemas/FileSystem"},"http":{"$ref":"#/components/schemas/Http"},"file":{"$ref":"#/components/schemas/File"},"salesforce":{"$ref":"#/components/schemas/Salesforce"},"as2":{"$ref":"#/components/schemas/AS2"},"dynamodb":{"$ref":"#/components/schemas/DynamoDB"},"ftp":{"$ref":"#/components/schemas/FTP"},"jdbc":{"$ref":"#/components/schemas/JDBC"},"mongodb":{"$ref":"#/components/schemas/MongoDB"},"netsuite":{"$ref":"#/components/schemas/NetSuite"},"rdbms":{"$ref":"#/components/schemas/RDBMS"},"s3":{"$ref":"#/components/schemas/S3"},"wrapper":{"$ref":"#/components/schemas/Wrapper"},"parsers":{"$ref":"#/components/schemas/Parsers"},"filter":{"description":"Filter applied to each record after the export's transform and before its preSavePage\nhook. Records that match continue through the flow; records that don't are silently\ndropped. Filter expressions reference the transformed record's fields.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"inputFilter":{"description":"Filter applied to incoming records before a lookup export queries the external system.\nOnly matching input records trigger lookup calls; non-matching records pass through the\nstep unenriched, which cuts unnecessary API traffic. Only relevant when `isLookup` is true.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"mappings":{"description":"Field mappings applied to each incoming record before a lookup HTTP request is made,\nreshaping the upstream record into the structure the lookup target API expects. Only\nsupported when `isLookup` is true and `adaptorType` is `HTTPExport` or\n`NetSuiteHTTPExport`; do not set it on source exports or other lookup adaptors.","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"transform":{"description":"Transformation that reshapes records before they leave this step — source exports\ntransform the retrieved records, lookup exports transform the lookup results. Commonly\nused to flatten nested lookup responses (e.g. `results[0].id` → `id`); merging results\nback into source records is handled separately by the flow's response mapping.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the export lifecycle for\ntransformations, validation, and filtering beyond what configuration alone can express.\nA hook name not listed here (an import's `preMap`, for example) is dropped on save\nwithout an error, so read `hooks` back after saving.","properties":{"preSavePage":{"type":"object","description":"Hook that runs on each page of records after the export's transform and output\nfilter, before the page is sent to downstream steps; commonly used to flatten\nstructures, drop unwanted records, or add computed fields. It receives the page as\n`options.data` (with `errors`, `pageIndex`, `configuration` and `settings`) and returns\n`{data, errors, abort, newErrorsAndRetryData}`. Return that object, not the array: a\nhook that returns `options.data` itself, or a filtered copy, drops the page without an\nerror, and the run completes with 0 records, 0 errors and 0 pages. An exception thrown\nhere fails every record on that page, each with its own retry data, and later pages\nstill run.","properties":{"function":{"type":"string","description":"Function to invoke within the referenced script."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`. Must be a script in this\naccount: any other id fails the save with 422 (\"Only a script owned by the user or\na script shared by the Integration App can be used at hooks.preSavePage._scriptId.\")."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments."},"configuration":{"type":"object","description":"Static parameters passed to the hook function at runtime, letting one script be\nreused across exports with different settings."}}},"preParse":{"type":"object","description":"Hook that runs on file-based exports after the file is downloaded and before it is\nparsed: it receives the raw file text as `preParseData` together with `fileMeta` and\nreturns both, modified or not, for the configured parser to read. Used to normalize a\nnon-standard file — fix a header row, strip a preamble, convert a format — before\nparsing; an exception thrown here fails the run.","properties":{"function":{"type":"string","description":"Function to invoke within the referenced script."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments."},"configuration":{"type":"object","description":"Static parameters passed to the hook function at runtime, letting one script be\nreused across exports with different settings."}}}}},"settingsForm":{"$ref":"#/components/schemas/Form"},"settings":{"$ref":"#/components/schemas/Settings"},"mockOutput":{"$ref":"#/components/schemas/MockOutput"},"_ediProfileId":{"type":"string","format":"objectId","description":"EDI profile this export uses to parse incoming X12 or EDIFACT documents into structured\nJSON — it supplies the envelope qualifiers, delimiters, version, and validation rules.\nSet it when the export parses or validates EDI files (a save with a `delimited/x12` file\ndefinition at `version` `\"2\"` and no profile fails with 422 `missing_ediProfile`); omit\nit otherwise. Accepted on the file-reading exports — FTP, AS2, S3 and HTTP file mode\n(`http.type: \"file\"`)."},"_postParseListenerId":{"type":"string","format":"objectId","description":"Webhook export invoked once per file after EDI parsing, on both success and failure\n(errors are included in the payload when parsing fails). Primarily used to send\nfunctional acknowledgements (997/999) to trading partners. Accepted on the file-reading\nexports that parse EDI — FTP, AS2, S3 and HTTP file mode (`http.type: \"file\"`)."},"externalId":{"type":["string","null"],"description":"External identifier for correlating the export with a record in another system."},"_integrationId":{"type":["string","null"],"format":"objectId","description":"Not stored on an export: a value sent on create or on a full update is dropped, and a GET\ndoesn't return the field. An export isn't owned by an integration; to find the flows and\nother resources that use it, call `GET /v1/exports/{_id}/dependencies`."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this export was created from, set when the export is part of an installed\nintegration app. The app's install sets it: a create that sends it is refused with 403\n(\"Cannot create an Integration App resource\")."},"unencrypted":{"type":"object","description":"Custom configuration values stored without encryption and returned in API responses."},"useTechAdaptorForm":{"type":"boolean","description":"When true, the UI presents the full technical adaptor form for this export instead of\nthe simplified assistant form."},"rawData":{"type":"string","description":"Key referencing the raw sample payload captured for preview and testing."},"preSave":{"$ref":"#/components/schemas/PreSave"},"assistant":{"type":"string","description":"Identifier for the connector assistant used to configure this export. For the cloud\nfile providers it also selects the provider-specific location rules —\n`azuredatalakestoragegen2` (container in `file.directory.name`, path in\n`http.relativeURI`) and `googlecloudstorage` (bucket in `file.directory.storageRootId`,\nprefix in `file.directory.id`)."},"assistantMetadata":{"type":["object","string"],"additionalProperties":true,"description":"Metadata associated with the connector assistant configuration."},"sampleData":{"type":["string","object","array","null"],"description":"Sample data payload used for previewing and testing the export."},"sampleHeaders":{"type":"array","description":"Sample HTTP headers used for previewing and testing the export.","items":{"type":"object","properties":{"name":{"type":"string","description":"Header name."},"value":{"type":"string","description":"Header value."}}}},"sampleQueryParams":{"type":"array","description":"Sample query parameters used for previewing and testing the export.","items":{"type":"object","properties":{"name":{"type":"string","description":"Query parameter name."},"value":{"type":"string","description":"Query parameter value."}}}}}},"OneToMany":{"type":"boolean","description":"When true, the step runs once per child record instead of once per incoming record.\n`pathToMany` names the array field that holds the children inside an object record; when\nthe incoming record is itself an array (grouped or row-based data), leave `pathToMany` blank\nand each element becomes a record. The fan-out is scoped to this step: afterwards the children\nare re-joined into the record's original shape — the object with its array, or the array of rows —\ncarrying any response-mapping enrichment, and that re-joined record is what the next step\nreceives. Not for locating records in an export's HTTP response; use\n`http.response.resourcePath` for that. Applies to steps that receive a record — imports and\nlookups (an export referenced as a page processor of a flow, API, or Tool). A standalone export\n(a flow's page generator) has no incoming record, so the fields are saved but have no effect on\nthe records it produces; to emit one record per array element from a standalone export, use a\n`hooks.preSavePage` script, or for a file-definition export make the repeating segment the\nrecord boundary in the definition's rules.\n","default":false},"PathToMany":{"type":"string","description":"Path to the array of child records inside an object record when `oneToMany` is true, in dot\nnotation (`items`, `lines.lineItems`). Leave blank when the incoming record is itself an array\n(grouped or row-based data) — each element is then a child record. A path that does not resolve\nto an array processes zero records and reports success. Read only on steps that receive a\nrecord (imports, lookup exports); on a standalone export it is saved and ignored — see `oneToMany`.\n"},"GroupBy":{"type":"array","description":"Specifies which fields to use for grouping records in the export results. When configured, records with\nthe same values in these fields will be grouped together and treated as a single record by downstream\nsteps in your flow.\n\nFor example:\n- Group sales orders by customer ID to process all orders for each customer together\n- Group journal entries by accounting period to consolidate related transactions\n- Group inventory items by location to process inventory by warehouse\n\nWhen grouping is used, the export's page size determines the maximum number of groups per page, not individual\nrecords. Note that effective grouping typically requires that records with the same group field values appear\ntogether in the export data.\n\nNot a webhook listener feature: on a `webhook` export the platform applies it only within each page, after\nthe request has been paged, and only to adjacent rows — the field is not offered for listeners in the UI.\n","items":{"type":"string"}},"Delta":{"type":"object","description":"Configures incremental exports that retrieve only records created or modified since the\nflow's delta checkpoint; omit it for other export types. Adaptors that compare a record field\nname it in `dateField`, while HTTP exports and SQL queries carry {{lastExportDateTime}} in the\nrequest and save without a delta object. Each run's window runs from {{lastExportDateTime}}\n(the checkpoint) to {{currentExportDateTime}} (the time the run started). A first run is not a\nfull export: with no `startDate`, its {{lastExportDateTime}} is the run's own timestamp, so it\npicks up no earlier changes. The checkpoint moves to a run's window end only when the run\nexports records, so a run that exports nothing leaves it where it was, and after a failed run\nthe next run reuses the last checkpoint. On success, when `POST /v1/flows/{_id}/run` sends\n`export.endDate`, the flow's checkpoint is left at that value, so an end date in the past\nmoves the next run's lower bound back.","properties":{"dateField":{"type":"string","description":"Record timestamp field(s) compared against the last successful run time to identify\nchanged records. Accepts a single field or multiple comma-separated fields, processed\nsequentially — useful when different operations update different timestamp fields.\nIf the flow's own downstream steps update the exported records, add export criteria\nthat exclude already-processed records (or use a creation-time field for process-once\nflows) so each run doesn't re-export what the previous run wrote. Not supported on\nHTTP exports: embed {{lastExportDateTime}} in the relativeURI or body instead; an HTTP\nexport saved with dateField is refused with 422 \"Unsupported dateField in\nexport.delta.dateField\". For Salesforce this\nfield is required and defaults to the standard timestamp fields (LastModifiedDate,\nCreatedDate, SystemModstamp, LastActivityDate, LastViewedDate, LastReferencedDate) plus\nany custom timestamp fields."},"dateFormat":{"type":"string","description":"Moment.js format string applied to both delta variables, {{lastExportDateTime}} and\n{{currentExportDateTime}}, in HTTP requests and SQL queries alike. Leave unset unless the\nsource system requires a non-ISO8601 format; by default both render as ISO 8601 UTC with\nmilliseconds. Date-only formats truncate the time portion, widening the filter window."},"lagOffset":{"type":"integer","description":"Buffer in milliseconds subtracted from the last successful run timestamp, creating an\noverlapping window that catches records still propagating when the previous run executed.\nSet it when records created or modified near the run time are occasionally skipped due to\nreplication or indexing delays. Keep it as low as possible — larger values reprocess\nredundant records. A negative value shifts the window forward (look-ahead) instead of back."},"startDate":{"type":["string","null"],"format":"date-time","description":"Lower-bound cutoff for the first run. Omitting it doesn't export history: the first run\nthen starts at its own timestamp, an empty window. Set it to the earliest change the first\nrun should pick up. Later runs start from the flow's checkpoint, the end of the last run\nthat exported records."}}},"Test":{"type":"object","description":"Configures test exports that cap the number of records retrieved, for safely developing and\nvalidating against small data samples. Required when the export's type is \"test\"; omit for\nother export types. A test export behaves like a standard export in every other way (filters,\npagination, processing) and stores no state between runs.","properties":{"limit":{"type":"integer","default":1,"description":"Caps the total records a test run processes, counted on top-level records before\noneToMany processing; the maximum exists to prevent accidentally processing large\ndatasets during development. The cap applies across pages — processing stops once the\nlimit is reached regardless of pageSize. When transitioning to production, leave this in\nplace and change the export's type field; the limit then no longer applies.","minimum":1,"maximum":100}}},"Once":{"type":"object","description":"Configures flag-based exports that skip records an earlier run already processed. Required\nwhen the export's type is \"once\"; omit for other export types. Each run retrieves records\nwhere the tracking boolean field is false, then sets that field to true after successful\nprocessing so later runs skip them; if a run fails, the flags are left unchanged and the\nrecords are retried automatically on the next run.","properties":{"booleanField":{"type":"string","description":"API field name of the boolean/checkbox in the source system that tracks processed\nrecords: the export selects only records where this field is false, then sets it to true\nin batches after each successfully processed page. The field must be writeable by the\nexport's connection. Ensure no other process updates the same field — use a separate\nflag per export process — or records may be skipped unexpectedly."}}},"Webhook":{"type":"object","description":"Configuration for webhook listeners that receive data through incoming HTTP requests. Required when the export's `type` is `webhook`; omit for other export types. Senders POST to the export's listener URL, `https://api.integrator.io/v1/exports/{_id}/data` (or the same path on the account's regional API host); the platform validates each request using the configured `verify` method, passes the payload to subsequent flow steps, and returns a configurable HTTP response to the caller. A request is answered 204 by default when accepted, 401 when it fails verification, and 403 `unable_to_run_flows` while every flow the export feeds is disabled; a refused request is not queued, so the sender must resend it.","properties":{"provider":{"type":"string","description":"Source application sending the webhook, used to pre-configure security settings and payload parsing for that platform. Use `custom` (the default when omitted) for unlisted providers or when you need full manual control over the security configuration. Provider-specific choices may require credentials (tokens, keys) mandated by that platform.","enum":["github","shopify","travis","travis-org","slack","dropbox","onfleet","helpscout","errorception","box","stripe","aha","jira","pagerduty","postmark","mailchimp","intercom","activecampaign","segment","recurly","shipwire","surveymonkey","parseur","mailparser-io","hubspot","integrator-extension","custom","sapariba","happyreturns","typeform"]},"verify":{"type":"string","description":"Verification method applied to every incoming request before processing; required for all webhook exports. Each method needs companion fields: `hmac` requires `key`, `algorithm`, `encoding`, and `header` (except on connector-backed webhooks, where the connector definition supplies them); `token` requires `token`, with `tokenLocation` defaulting to `body` and selecting which location-specific field applies; `basic` requires `username` and `password`; `secret_url` requires `token`. Prefer `hmac` when the source system supports it — it is the most secure option; `secret_url` relies only on URL obscurity and suits non-sensitive data or testing.","enum":["token","hmac","basic","secret_url"]},"token":{"type":"string","description":"Shared secret used when `verify` is `token` or `secret_url`. For `token` verification, the value found at `path` in each request must exactly match (case- and whitespace-sensitive) or the request is rejected with a 401. For `secret_url`, the token is embedded in the webhook URL to create a hard-to-guess endpoint — generate a random, high-entropy value and treat it as a sensitive credential."},"algorithm":{"type":"string","description":"Hashing algorithm used to validate HMAC signatures when `verify` is `hmac`. Must match the algorithm the webhook sender uses — a mismatch causes every request to be rejected. Use `sha256` unless the provider explicitly requires another value.","enum":["sha1","sha256","sha384","sha512"]},"encoding":{"type":"string","description":"Encoding of the HMAC signature value when `verify` is `hmac`. Must match the encoding the webhook sender uses — a mismatch causes requests to be rejected with a 401 even when the signature is otherwise correct. With `provider: custom`, `hex` is compared as lowercase hex: an uppercase hex signature is rejected.","enum":["hex","base64"]},"key":{"type":"string","description":"Secret used to validate signatures when `verify` is `hmac`. The system computes a signature of the request body using this key and the configured `algorithm`, then compares it with the signature sent in the request header named by `header`. Treat it as a highly sensitive credential — never expose it in repositories or logs."},"header":{"type":"string","description":"Name of the request header that carries the HMAC signature when `verify` is `hmac`. Must match the header name the webhook sender uses (header names are case-insensitive); requests without this header are rejected with a 401. With `provider: custom` the header value must be the bare digest in the configured `encoding`: a value with a `sha256=` prefix is rejected with a 401."},"tokenLocation":{"type":"string","enum":["body","header","queryParam"],"default":"body","description":"Where the verification token is found when `verify` is `token`. Each location uses a different companion field: `body` uses `path`, `header` uses `tokenHeaderName` (and `tokenHeaderScheme`), `queryParam` uses `tokenQueryParamName`."},"path":{"type":"string","description":"JSON path into the request body holding the verification token when `verify` is `token` and `tokenLocation` is `body` (e.g. `meta.token`). The value at this path must exactly match `token` or the request is rejected."},"tokenHeaderName":{"type":"string","description":"Request header carrying the verification token when `verify` is `token` and `tokenLocation` is `header`."},"tokenHeaderScheme":{"type":"string","enum":["bearer","custom","none"],"default":"bearer","description":"Scheme prefixing the token in the header when `tokenLocation` is `header`."},"customTokenScheme":{"type":"string","description":"Custom header scheme prefix used when `tokenHeaderScheme` is `custom`."},"tokenQueryParamName":{"type":"string","description":"Query parameter carrying the verification token when `verify` is `token` and `tokenLocation` is `queryParam`."},"_httpConnectorId":{"type":"string","format":"objectId","description":"HTTP connector backing this listener when the webhook is provided by an assistant/connector (e.g. Slack); set by the platform for connector-backed listeners."},"requestMediaType":{"type":"string","enum":["json","xml","csv","urlencoded","plaintext"],"description":"Overrides how the incoming request body is parsed when the provider sends a non-standard content type. When omitted, the body is parsed by its Content-Type header."},"pathToRecords":{"type":"string","description":"JSON path into an object payload to the array whose elements become the records. When omitted, a top-level JSON array is emitted as one record per element and an object payload is emitted as a single record."},"includeParentData":{"type":"boolean","description":"When true and `pathToRecords` is set, each emitted record also carries the surrounding parent fields from the payload."},"username":{"type":"string","description":"Username half of the credentials when `verify` is `basic`. Incoming requests must include an `Authorization: Basic` header carrying the base64-encoded `username:password` pair. Use only over HTTPS to prevent credential interception."},"password":{"type":"string","description":"Password half of the credentials when `verify` is `basic`, validated together with `username` from the request's `Authorization` header. Use a strong, unique value and treat it as a sensitive credential."},"successStatusCode":{"type":"integer","description":"HTTP status code returned to the caller after successful processing; must be a valid 2xx code. The default 204 returns no response body and causes `successBody` to be ignored — set 200 or 202 when the caller needs a response body or expects a specific code.","default":204},"successBody":{"type":"string","description":"Response body returned to the caller after successful processing; ignored when `successStatusCode` is 204. Content type is set by `successMediaType`, and the value can be static text or structured JSON/XML, including handlebars expressions for dynamic values."},"successMediaType":{"type":"string","description":"Sets the Content-Type header on successful webhook responses. Only takes effect when a `successBody` is returned (a status code other than 204), and must match the actual format of that body.","default":"json","enum":["json","xml","plaintext"]},"successResponseHeaders":{"type":"array","description":"Custom headers added to successful webhook responses — for example CORS headers or correlation IDs. Headers defined here take precedence over automatically set headers such as Content-Type, and values support handlebars expressions for dynamic content.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the header to set on successful webhook responses; headers defined here take precedence over automatically set headers such as Content-Type."},"value":{"type":"string","description":"Value sent for the header; supports handlebars expressions for dynamic content."}}}},"challengeResponseHeaders":{"type":"array","description":"Custom headers returned for webhook subscription verification (challenge) requests, which providers send before delivering real events. Required header values vary by provider — consult the provider's documentation, since incorrect challenge headers can prevent the subscription from being verified.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the header to set on challenge (subscription verification) responses."},"value":{"type":"string","description":"Value sent for the header; consult the provider's documentation for required challenge header values."}}}},"challengeSuccessBody":{"type":"string","description":"Response body returned for webhook subscription verification (challenge) requests. Many providers require echoing back a challenge value from the request, which handlebars expressions can access — for example `{{hub.challenge}}` (Facebook/Instagram) or `{\"challenge\":\"{{challenge}}\"}` (Slack). An incorrect challenge response prevents the subscription from being verified."},"challengeSuccessStatusCode":{"type":"integer","description":"HTTP status code returned for webhook subscription verification (challenge) requests. Most providers expect the default 200; change it only when the provider's verification explicitly requires a different code.","default":200},"challengeSuccessMediaType":{"type":"string","description":"Sets the Content-Type header on challenge responses. Must match both the format of `challengeSuccessBody` and the provider's requirements — for example, Slack expects `json` while Facebook/Instagram verification expects `plaintext`.","default":"json","enum":["json","xml","plaintext"]}},"if":{"properties":{"verify":{"const":"hmac"}},"required":["verify"],"not":{"required":["_httpConnectorId"]}},"then":{"required":["key","algorithm","encoding","header"]},"else":{"if":{"properties":{"verify":{"const":"token"}},"required":["verify"]},"then":{"required":["token"],"if":{"properties":{"tokenLocation":{"const":"body"}},"required":["tokenLocation"]},"then":{"required":["path"]},"else":{"if":{"properties":{"tokenLocation":{"const":"header"}},"required":["tokenLocation"]},"then":{"required":["tokenHeaderName"]},"else":{"if":{"properties":{"tokenLocation":{"const":"queryParam"}},"required":["tokenLocation"]},"then":{"required":["tokenQueryParamName"]},"else":{"required":["path"]}}}},"else":{"if":{"properties":{"verify":{"const":"basic"}},"required":["verify"]},"then":{"required":["username","password"]},"else":{"if":{"properties":{"verify":{"const":"secret_url"}},"required":["verify"]},"then":{"required":["token"]}}}}},"Simple":{"type":"object","description":"Configuration for `simple` (data-loader) exports — exports that accept manually uploaded\nfiles through the data-loader UI instead of fetching data from a connection. Relevant only\nwhen the export's `type` is `simple`; the uploaded content is parsed using the same file\nsettings as file-based exports.","properties":{"file":{"$ref":"#/components/schemas/File"}}},"File":{"type":"object","description":"Controls how files are parsed, filtered, and processed across all file-based exports\n(FTP/SFTP, Amazon S3, simple file uploads, and other file sources). The type field selects\nthe file format and determines which format-specific object (csv, json, xlsx, xml, or\nfileDefinition) must be configured; the output field selects whether files are parsed into\nrecords, transferred as blobs, or listed as metadata only. The filter object selectively\nskips files before processing.","properties":{"encoding":{"type":"string","description":"Character encoding used to read and parse file content. If the encoding is unknown, try\nutf8 first (the default), then win1252 for Western-language files with garbled\ncharacters; consider the geographic origin of the data when selecting.","enum":["utf8","win1252","utf-16le","gb18030","macroman","iso88591","shiftjis"]},"type":{"type":"string","description":"Format of the files being processed; determines which format-specific configuration\nobject (csv, json, xlsx, xml, or fileDefinition) must be populated — other format\nobjects are ignored. Required for all file-based exports except blob exports (export\ntype \"blob\" or output \"blobKeys\").","enum":["csv","json","xlsx","xml","filedefinition"]},"output":{"type":"string","description":"Processing mode for retrieved files: parse contents into records, transfer files as\nunparsed blobs, or return only file metadata. Determines what data is passed to\nsubsequent flow steps.","enum":["records","metadata","blobKeys"]},"skipDelete":{"type":"boolean","default":false,"description":"When true, source files remain on the file server after processing; when false (the\ndefault), files are deleted after successful processing. Files that fail processing are\nnever deleted, regardless of this setting. Enable retention when files must be processed\nby other flows or systems, or kept for compliance."},"compressionFormat":{"type":"string","description":"Compression format of incoming files, which are decompressed before any other processing\n(parsing, filtering). Set this only when the source always delivers compressed files —\nif a file marked as compressed is not actually compressed, processing fails. Leave unset\nwhen files arrive uncompressed or only sometimes compressed.","enum":["gzip","zip"]},"purgeInternalBackup":{"type":"boolean","description":"When true, Celigo keeps no internal backup copies of files processed by this export;\nwhen false (the default), copies are retained for your account's retention period and\nare available for reprocessing or troubleshooting. Applies only to this export and only\nto Celigo's internal copies — source files are governed by skipDelete. Enable for highly\nsensitive data or zero-retention policies; without backups, recovery may require\nre-obtaining files from the source system."},"decrypt":{"type":"string","description":"Decryption applied to incoming files before any other processing; decryption runs before\ndecompression, and a decryption failure fails the file's processing entirely. The\nconnection must already be configured with the private key (and passphrase, if\napplicable) matching the public key used to encrypt the files. Only PGP/GPG encryption\nis currently supported.","enum":["pgp"]},"batchSize":{"type":["integer","null"],"description":"Number of files retrieved per batch; if a batch fails, the whole batch is retried\n(stored as `null` when not configured). Use\nlower values (10-50) for large files to reduce timeout and memory pressure, and higher\nvalues for many small files to improve throughput. Controls file retrieval only — record\npaging is governed by the export's pageSize.","maximum":1000},"sortByFields":{"type":"array","description":"Sorts the records parsed from each file before they are processed, establishing a\ndeterministic processing order (for example, chronological or priority-based). Sorting\nhappens after parsing but before any filtering or grouping, and does not modify the\nsource files. Sorting by the same fields used in groupByFields improves grouping\nperformance.","items":{"type":"object","properties":{"field":{"type":"string","description":"Record field to sort by; use dot notation for nested properties (e.g.\ncustomer.name). Field names are case-sensitive."},"descending":{"type":"boolean","description":"When true, sorts this field in descending order (newest/highest first); when false\nor omitted, sorts ascending. Directions can be mixed across fields in a\nmulti-field sort."}}}},"groupByFields":{"$ref":"#/components/schemas/GroupBy"},"groupEmptyValues":{"type":"boolean","description":"When true, records whose groupByFields values are empty are still grouped together rather\nthan each forming its own group; when false (the default), empty-keyed records are not\ngrouped. Only relevant when groupByFields is set."},"csv":{"type":"object","description":"Parsing settings for delimiter-separated text files (CSV, TSV, pipe-delimited, and\nsimilar). Configure when type is \"csv\".\n\nRequired when type is csv.","properties":{"columnDelimiter":{"type":"string","description":"Character sequence separating fields within each row; comma when omitted. Use \"\\t\"\nfor tab-delimited files; European-locale exports often use semicolons. A wrong\ndelimiter doesn't fail the parse: with a header row, each row becomes one field named\nafter the whole header line (`{\"order_id;sku;qty\": \"1001;TEE-M;2\"}`), so the failure\nshows up in later steps."},"rowDelimiter":{"type":"string","description":"Character sequence marking the end of each record; auto-detected from file content\nwhen omitted. Set explicitly (\"\\n\", \"\\r\\n\", or \"\\r\") only when auto-detection fails,\nsuch as for files with mixed line endings — an incorrect value merges or splits\nrecords."},"hasHeaderRow":{"type":"boolean","description":"When true (the default), the first row is read as field names rather than data, and\nthose names are available in mappings. When false, every row is treated as a data\nrecord and fields are referenced by position."},"trimSpaces":{"type":"boolean","description":"When true, removes leading and trailing whitespace from every field value during\nparsing; when false (the default), whitespace is preserved exactly as in the source.\nHeader row values are always trimmed regardless of this setting, and spaces between\nwords are never affected."},"rowsToSkip":{"type":"integer","description":"Number of rows at the top of the file to ignore before parsing begins — useful for\nreport titles, timestamps, or other metadata above the data. The header row (when\nhasHeaderRow is true) is expected after the skipped rows."},"disableQuoteAndStripEnclosingQuotes":{"type":"boolean","description":"When true, disables CSV quote processing: quotes are treated as literal characters,\nenclosing quotes are stripped, and delimiters inside quoted text split the field.\nWhen false (the default), standard RFC 4180 quoting applies and quoted fields\nprotect embedded delimiters. Enable only for files with non-standard or malformed\nquoting; review this setting first when field counts vary unexpectedly between rows."},"keyColumns":{"type":"array","description":"Field names whose values together identify a record's group when consecutive rows\nshare the same key — used to merge multi-line records (e.g. an order header repeated\nacross line-item rows) into a single record. Leave empty for one record per row; the\ncolumns must exist in the parsed header.","items":{"type":"string"}}}},"json":{"type":"object","description":"Parsing settings for JSON files. Configure when type is \"json\". resourcePath locates the\narray of records when they are nested inside a container object; malformed JSON fails\nthe entire file's processing.\n\nRequired when type is json.","properties":{"resourcePath":{"type":"string","description":"Dot-notation path to the array of records within the JSON structure (e.g.\n\"response.data.customers\"); leave empty when the file's root is already the record\narray. The path must resolve to an array of objects — array indexing and wildcards\nare not supported. A path that resolves to nothing produces zero records without\nraising an error."}}},"xlsx":{"type":"object","description":"Parsing settings for Microsoft Excel (.xlsx) workbooks. Configure when type is \"xlsx\".\nCalculated cell values are extracted rather than formulas; legacy .xls files are not\nsupported — use the modern Open XML format.\n\nRequired when type is xlsx.","properties":{"hasHeaderRow":{"type":"boolean","description":"When true (the default), the first row is read as field names rather than data;\nblank header cells are auto-named and duplicate names are made unique with suffixes.\nWhen false, every row is treated as data and fields receive generic positional names\n(Column1, Column2, ...)."}}},"xml":{"type":"object","description":"Parsing settings for XML documents. Configure when type is \"xml\". resourcePath is an\nXPath expression selecting the elements treated as records; namespaces are handled\nautomatically.\n\nRequired when type is xml.","properties":{"resourcePath":{"type":"string","description":"XPath expression selecting the elements treated as records — each matching element\nbecomes one record, with its child elements as fields. Required for XML parsing;\nthere is no default. Use an absolute path (/Root/Order) when the structure is fixed,\n//Element to match at any depth, or predicates (//Element[@type=\"product\"]) to\nfilter; XPath is case-sensitive, and a non-matching path yields zero records."}}},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"},"fileDefinition":{"type":"object","description":"Parsing via a predefined file definition resource, for formats the standard parsers\ncannot handle — fixed-width files, EDI documents (X12, EDIFACT), and multi-record-type\nor proprietary formats. Configure when type is \"filedefinition\".\n\nRequired when type is filedefinition.","properties":{"_fileDefinitionId":{"type":"string","format":"objectId","description":"File definition resource containing the parsing rules for this format. Must\nreference an existing, accessible file definition — never guess or fabricate the ID.\nDefinitions are reusable across exports, so changing one affects every export that\nuses it."},"allowPartialSuccess":{"type":"boolean","description":"When true, a file whose records partially fail parsing against the file definition\nstill emits the records that did parse, instead of failing the entire file. Applies to\nfile-definition (fixed-width, EDI) parsing only. For X12 and EDIFACT the unit is the\ntransaction set (ST–SE, UNH–UNT), and it takes effect only with an EDI profile linked:\na failed set is set aside and reported as an error while the file's other sets go on\nas records. Without this flag, one failed set fails the whole file."}}},"filter":{"description":"Selects which files are processed: files matching the filter criteria are exported,\nand non-matching files are skipped entirely before processing begins. Filterable\nfields come from each file's listing entry: the name as `fileName`, the size as\n`filesize`, and the modified time as `lastModifiedTime` on FTP, SFTP and S3 or\n`modifiedTime` on cloud-storage folders (`http.type: \"file\"`)\n(e.g. [\"endswith\", [\"string\", [\"extract\", \"fileName\"]], \".csv\"]). There is no\n`lastmodified` key.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"backupPath":{"type":"string","description":"Path where backup copies of source files are stored before processing."},"includeSubdirectories":{"type":"boolean","default":false,"description":"When true, an Azure Data Lake Storage Gen2 transfer (`assistant: azuredatalakestoragegen2`,\n`http.type: file`) also picks up files in the subdirectories below the directory path in\n`http.relativeURI`; when false or omitted only that directory's own files are transferred."},"directory":{"type":"object","required":["pathMode"],"description":"Structured source location for cloud file-provider sources (Google Drive shared drives,\nBox, Dropbox, Google Cloud Storage, Azure Data Lake Storage Gen2), replacing the flat path\nused by classic FTP/S3 sources. Select the location by folder ID or by a path relative to\na configured storage root via pathMode. For the drive-style providers, directoryId mode\naddresses the folder by id alone — the flat relative-path field is not consulted at\nruntime. Google Cloud Storage puts the bucket in `storageRootId` and the object prefix in\n`id` with `pathMode: directoryId`; Azure Data Lake Storage Gen2 puts the container in\n`name` and the directory path in `http.relativeURI`.","properties":{"pathMode":{"type":"string","enum":["relativePath","directoryId"],"description":"How the directory is addressed — by provider folder ID, or by a path relative to a configured storage root."},"id":{"type":"string","description":"Provider-native folder ID of the source directory (e.g. a Google Drive folder ID); set\nwhen pathMode is directoryId. This is the file provider's own ID, not a Celigo resource ID.\nFor Google Cloud Storage it is the object prefix inside the bucket, ending with `/`."},"name":{"type":"string","description":"Display name of the folder identified by id, kept for readability in the UI; the\nfolder browser fills it when the location is picked visually. For Azure Data Lake\nStorage Gen2 it is the authoritative container name instead — the form saves the\ncontainer here and no id."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root (e.g. a Google Drive shared-drive ID) the relative\npath resolves against; set when pathMode is relativePath. The provider's own ID, not a Celigo ID.\nFor Google Cloud Storage it is the bucket name."},"storageRootName":{"type":"string","description":"Display name of the storage root identified by storageRootId, retained for reference in the UI."}}},"backupDirectory":{"type":"object","required":["pathMode"],"description":"Structured destination for backup copies of source files on cloud file providers — the\nfile-provider counterpart to backupPath. Addressed the same way as directory.","properties":{"pathMode":{"type":"string","enum":["relativePath","directoryId"],"description":"How the backup location is addressed — by provider folder ID, or by a path relative to a configured storage root."},"id":{"type":"string","description":"Provider-native folder ID of the backup directory; set when pathMode is directoryId."},"name":{"type":"string","description":"Display name of the backup folder identified by id, kept for readability in the UI;\nthe folder browser fills it when the location is picked visually."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root the backup relative path resolves against; set when pathMode is relativePath."}}}}},"IncludeParentData":{"type":"boolean","description":"When true and a path to records is configured on the export (`pathToRecords` for MongoDB and DynamoDB, `http.response.resourcePath` for HTTP, `file.json`/`file.xml` `resourcePath` for file-based exports), the data outside that path is attached to each extracted record as a `_PARENT` object, keeping the surrounding envelope available to downstream steps. When omitted or false, records are emitted without `_PARENT`, exactly as before. Constraints: on HTTP exports this option cannot be combined with `useXpathParser: true` (rejected at save with `EXPORT_INCLUDE_PARENT_DATA_XPATH_UNSUPPORTED`), and XML responses must resolve to the default converted-JSON parse mode — a run whose XML parser resolves to DOM/XPath mode (for example, because a connection-level field uses an advanced XPath expression) fails at start with the same error rather than silently omitting `_PARENT`. On file-based exports, it requires `file.type` of `json` with `file.json.resourcePath` configured — XML file envelope capture is not yet supported (the streaming XML parser emits no envelope event; XML support arrives with a parsers-package enhancement) — and any other file configuration is rejected at save with `EXPORT_INCLUDE_PARENT_DATA_UNSUPPORTED_FILE_TYPE`."},"Filter":{"type":"object","description":"Configuration for selectively processing records based on specified criteria. This object enables\nprecise control over which items are included or excluded from processing operations.\n\n**Filter behavior**\n\nWhen configured, the filter is applied before processing begins:\n- Items that match the filter criteria are processed\n- Items that don't match are completely skipped\n- No partial processing is performed\n\nSend the whole object. A rules array sent alone as the `filter` value is not\nstored, and a full update of an export that carries one removes the filter the\nexport had, with no error.\n\n**Implementation approaches**\n\nThere are two distinct filtering mechanisms available:\n\n**Rule-Based Filtering (`type: \"expression\"`)**\n- **Best For**: Common filtering patterns based on standard attributes\n- **Capabilities**: Filter by names, values, dates, numerical ranges, text patterns\n- **Advantages**: Declarative, no coding required, consistent performance\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear, static criteria for selection\n\n**Script-Based Filtering (`type: \"script\"`)**\n- **Best For**: Complex logic, dynamic criteria, or business rules\n- **Capabilities**: Full programmatic control, access to complete metadata\n- **Advantages**: Maximum flexibility, can implement any filtering logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Simple rules aren't sufficient or logic needs to be dynamic\n","properties":{"type":{"type":"string","description":"Determines which filtering mechanism to use. This choice affects which properties\nmust be configured and how filtering logic is implemented.\n\n**Available types**\n\n**Rule-Based Filtering (`\"expression\"`)**\n- **Required Config**: The `expression` object with rule definitions\n- **Behavior**: Evaluates declarative rules against item attributes\n- **Best For**: Common patterns like name matching, date ranges, value limits\n- **Advantages**: Simpler to configure, no custom code required\n\n**Script-Based Filtering (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to determine which items to process\n- **Best For**: Complex conditions, business logic, dynamic criteria\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard filtering needs (name, size, date), use `\"expression\"`\n2. For complex logic or conditions not covered by expressions, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based filtering. This object enables filtering\nitems based on common attributes without requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define filtering rules that\ncan match against item attributes like name, type, value, date, and other properties.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Rules can be combined with AND/OR logic\n- Each rule can check a specific attribute\n- Multiple conditions can be applied (ranges, pattern matching, exact matches)\n\n**Common filter patterns**\n\n1. **Pattern matching**: Regular expressions with `matches`\n2. **Value range filtering**: Numbers between min and max values\n3. **Date range filtering**: Items created/modified within specific time ranges\n4. **Status checking**: Items with specific status values or properties\n\nFor AI agents: Rule-based filtering should be your first choice when the filtering criteria\ncan be expressed in terms of standard attributes. Only use script-based filtering when\nmore complex logic is required.\n","properties":{"version":{"type":"string","description":"Version identifier for the expression format. Currently only version \"1\" is supported.\n\nThis field ensures future compatibility if the expression format evolves. Always set to \"1\"\nfor current implementations.\n","enum":["1"]},"rules":{"type":"array","description":"Expression array defining filter conditions using prefix notation. The first element is the operator,\nfollowed by its operands which may themselves be nested expression arrays.\n\nThe rule expression follows this pattern:\n- First element: Operator name (string)\n- Remaining elements: Operands for that operator (values or nested expressions)\n\n**Expression structure**\n\nFilter expressions use a prefix notation where operators appear before their operands:\n```\n[operator, operand1, operand2, ...]\n```\n\n**Comparison Operators**\n- `\"equals\"`: Exact match (equals)\n- `\"notequals\"`: Not equal to value (not equals)\n- `\"greaterthan\"`: Value is greater than specified value (is greater than)\n- `\"greaterthanequals\"`: Value is greater than or equal to specified value (is greater than or equals)\n- `\"lessthan\"`: Value is less than specified value (is less than)\n- `\"lessthanequals\"`: Value is less than or equal to specified value (is less than or equals)\n- `\"startswith\"`: String starts with specified prefix (starts with)\n- `\"notstartswith\"`: String does not start with specified prefix (does not start with)\n- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"notendswith\"`: String does not end with specified suffix (does not end with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"empty\"`: Field is missing or empty (is empty)\n- `\"notempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches a regular expression, ignoring case (matches)\n\nOperator names are case-insensitive: `NOTEMPTY` and `endsWith` work and are stored\nas sent. `isempty`, `isnotempty`, `anyof`, `notcontains`, `doesnotstartwith`,\n`doesnotendwith`, `notmatches`, `doesnotmatch` and `notequal` are not operators:\nthey are refused with 422 \"Invalid operation: <name>\", with the name in lowercase,\nboth when the filter is saved and by `POST /v1/processors/filter`. To negate a\ncondition, wrap it in `not`.\n\nThe string comparisons (`equals`, `contains`, `startswith`, `endswith` and their\nnegations) are case-sensitive; wrap the field in `lowercase` to compare without case.\n`matches` is not: every pattern is compiled with the flags `gi`, so `^paid$` also\nmatches `PAID` and `[a-z]` matches capital letters. An inline flag such as `(?i)`\nmakes the pattern invalid. The filter still saves, but in a run every record fails\nwith `FILTER_EVALUATION_FAILED` (\"Invalid regular expression: /(?i)…/gi: Invalid\ngroup\"), and `POST /v1/processors/filter` refuses the call with 422 (live-verified\n2026-10-22).\n\n**Logical Operators**\n- `\"and\"`: All conditions must be true\n- `\"or\"`: At least one condition must be true\n- `\"not\"`: Negates the condition\n\n**Field Access and Type Conversion**\n- `\"extract\"`: Access a field from the item by name\n- `\"settings\"`: Access a custom setting from the flow, flow step, or integration configuration\n- `\"boolean\"`: Convert value to Boolean type\n- `\"epochtime\"`: Convert value to Epoch Time (Unix timestamp)\n- `\"number\"`: Convert value to Number type\n- `\"string\"`: Convert value to String type\n\n**Field Access Details**\n\n**Using `extract` to access record fields:**\n- Retrieves values from the current record being processed\n- Can access nested properties using dot notation (e.g., `\"customer.email\"`)\n- Paths are relative to the record itself: `[\"extract\", \"record.status\"]`\n  matches nothing\n- In a record filter, must be wrapped in a type conversion (`string`, `number`,\n  `boolean` or `epochtime`): a bare `[\"extract\", ...]` operand is refused with 422\n  \"Field operation extract must be called by a Type Conversion Operation but was called\n  by <operator>\", both when an import's filter is saved and by\n  `POST /v1/processors/filter`\n\n**Using `settings` to access configuration values:**\n- Retrieves values from the integration's configuration settings\n- Supports different scopes with prefix notation:\n  - `flow.settingName`: Access flow-level settings\n  - `flowGrouping.settingName`: Access the flow group's settings\n  - `export.settingName`: Access the export's (or lookup's) own settings\n  - `import.settingName`: Access the import's own settings\n  - `integration.settingName`: Access integration-level settings\n  - `connection.settingName`, `iClient.settingName`: Access the step's\n    connection and iClient settings\n- A scope whose resource has no settings can be absent; a rule that reads\n  it matches nothing\n- Must be wrapped in a type conversion like `extract`: a bare\n  `[\"settings\", ...]` operand is refused with 422 \"Field operation settings\n  must be called by a Type Conversion Operation\"\n- Useful for dynamic filtering based on configuration\n\n**Field Transformations**\n- `\"lowercase\"`: Convert string to lowercase\n- `\"uppercase\"`: Convert string to uppercase\n- `\"ceiling\"`: Round number up to the nearest integer\n- `\"floor\"`: Round number down to the nearest integer\n- `\"abs\"`: Get absolute value of a number\n\n**Arithmetic** (number operands)\n- `\"add\"`, `\"subtract\"`, `\"multiply\"`, `\"divide\"`, `\"modulo\"`: for example\n  `[\"subtract\", [\"number\", [\"extract\", \"total\"]], [\"number\", [\"extract\", \"discount\"]]]`.\n  `mod` is not an operator (422 \"Invalid operation: mod.\").\n\n**Dates**\n- `epochtime` reads ISO 8601 strings and epoch-millisecond numbers. An empty\n  string, a string that isn't a date, or epoch milliseconds sent as a string\n  fail it with \"Invalid date. Cannot convert given string or number to date\n  time.\", followed by `; record=` and the record as JSON: in a run that record fails (`FILTER_EVALUATION_FAILED`, source\n  `output_filter` on an export), and `POST /v1/processors/filter` and an\n  export preview refuse the whole call with 422. `and` and `or` evaluate\n  every operand, so a `notempty` test beside it doesn't prevent the failure.\n  A missing or null field doesn't match.\n- `datetime`, `date` and `timestamp` are not type conversions: 422\n  \"Invalid operation: <name>.\"\n- `number` reads a missing or empty field as 0.\n\n**Handlebars operands**\n- An operand can also be a string holding exactly one Handlebars expression, such as\n  `\"{{record.status}}\"` or `\"{{lowercase record.code}}\"`, on either side of the\n  operator. It renders against the record (`record.<field>`; an unprefixed\n  `{{status}}` renders empty) before the comparison.\n- The rendered value is a string: wrap it in `[\"number\", ...]` to compare it with a\n  number, or the rule is refused with 422.\n- `epochtime` does not render Handlebars: `[\"epochtime\", \"{{timestamp}}\"]` is refused\n  with 422 \"invalid datetime format\".\n- A string with text around the expression, or with more than one expression, is\n  compared as a literal.\n- `[\"number\", \"{{timestamp \\\"x\\\"}}\"]` is the current time in epoch milliseconds,\n  so `[\"greaterthanequals\", [\"epochtime\", [\"extract\", \"created\"]], [\"subtract\",\n  [\"number\", \"{{timestamp \\\"x\\\"}}\"], 604800000]]` keeps records created in the\n  last 7 days.\n- An unknown helper fails the rule with 422 \"Failed to evaluate handlebars\n  expression\".\n\nChoose the type conversion that matches the comparison — `number` for numeric\ncomparisons, `epochtime` for dates, `string` for text. For example:\n\n```json\n[\n  \"equals\",\n  [\n    \"number\",  // Convert to number before comparison\n    [\n      \"extract\",\n      \"quantity\"\n    ]\n  ],\n  100\n]\n```\n\nExample with datetime conversion:\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",  // Convert to Unix timestamp before comparison\n    [\n      \"extract\",\n      \"createdDate\"\n    ]\n  ],\n  1609459200000  // January 1, 2021 as Unix timestamp in milliseconds\n]\n```\n\nExample with transformations:\n```json\n[\n  \"and\",\n  [\n    \"matches\",\n    [\n      \"lowercase\",  // Convert to lowercase before matching\n      [\n        \"string\",\n        [\n          \"extract\",\n          \"categories\"\n        ]\n      ]\n    ],\n    \"netsuite\"\n  ],\n  [\n    \"notequals\",\n    [\n      \"string\",\n      [\n        \"extract\",\n        \"recurrence.pattern.type\"\n      ]\n    ],\n    \"\"\n  ]\n]\n```\n\nExample comparing a record field with a flow setting:\n```json\n[\n  \"equals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"trantype\"\n    ]\n  ],\n  [\n    \"string\",\n    [\n      \"settings\",\n      \"flow.trantype\"\n    ]\n  ]\n]\n```\n\n**Examples**\n\nExample 1: Status field is not equal to \"cancelled\"\n```json\n[\n  \"notequals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"status\"\n    ]\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"filename\"\n    ]\n  ],\n  \"HC\"\n]\n```\n\nExample 3: Amount is greater than 100\n```json\n[\n  \"greaterthan\",\n  [\n    \"number\",\n    [\n      \"extract\",\n      \"amount\"\n    ]\n  ],\n  100\n]\n```\n\nExample 4: Order date is after January 1, 2023 (UTC). Compare dates as `epochtime`\nagainst epoch milliseconds: a `string` comparison of ISO timestamps orders values with\ndifferent UTC offsets wrongly.\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",\n    [\n      \"extract\",\n      \"orderDate\"\n    ]\n  ],\n  1672531200000\n]\n```\n","items":{"oneOf":[{"title":"String","type":"string"},{"title":"Number","type":"number"},{"title":"Boolean","type":"boolean"},{"title":"Object","type":"object"},{"title":"Array","type":"array"},{"title":"Null","type":"null"}]}}}},"script":{"type":"object","description":"Configuration for programmable script-based filtering. This object enables complex, custom\nfiltering logic beyond what expression-based filtering can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to determine which items\nshould be processed.\n\n**Implementation approach**\n\nScript-based filtering works by:\n1. Executing the specified function from the referenced script\n2. Passing item data to the function\n3. Using the function's return value (true/false) to determine inclusion\n\n**Common use cases**\n\nScript filtering is ideal for:\n- Complex business logic that can't be expressed as simple rules\n- Dynamic filtering criteria that change based on external factors\n- Content-based filtering that requires deep inspection\n- Advanced pattern matching beyond simple wildcards\n- Multi-stage filtering with intermediate logic\n\nFor AI agents: Only use script-based filtering when expression-based filtering is insufficient.\nScript filtering requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to the Script resource that contains the filtering logic. This must be a valid\nObjectId of a Script resource in this account: any other id fails the save with 422\n(\"Only a script owned by the user or a script shared by the Integration App can be used\nat filter.script._scriptId.\").\n\nThe referenced script must contain the function specified in the `function` field\nand must be written to handle filtering specifically. The script receives\nitem data as its input and must return a boolean value indicating whether\nto process the item (true) or skip it (false).\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n"},"function":{"type":"string","description":"Name of the function within the script to execute for filtering decisions. This function\nmust exist in the script referenced by _scriptId.\n\n**Function requirements**\n\nThe specified function must:\n- Accept item data as its first parameter\n- Return a boolean value (true to process the item, false to skip it)\n- Handle errors gracefully\n- Execute efficiently (as it may run for many items)\n\n**Function signature**\n\n```javascript\nfunction filterItems(itemData) {\n  // itemData contains properties of the item being evaluated\n  // Custom logic here\n  return true; // or false to skip the item\n}\n```\n\nFor AI agents: Ensure the function name exactly matches a function defined in the\nreferenced script, as mismatches will cause the filter to fail.\n"}}}}},"Distributed":{"type":"object","description":"Authentication settings for distributed (real-time listener) exports.","properties":{"bearerToken":{"type":"string","description":"Bearer token that a system pushing data into this export sends as\n`Authorization: Bearer <token>`. Optional — most distributed listeners (NetSuite,\nSalesforce real-time) use no token. Write-only and stored encrypted; treat it as a\nsensitive credential.","format":"password"}}},"Cdc":{"type":"object","description":"Change-data-capture configuration for real-time listener exports (the \"Listen for real-time\ndata\" source step). Present only when `type` is `stream`. Set on `MongodbExport` (MongoDB change\nstreams) and `RDBMSExport` (SQL Server CDC and PostgreSQL logical replication); the listener\nstreams change events continuously instead of fetching records on a schedule.\n\nOn save the API normalizes the `properties` grid into the typed fields: a `snapshot.mode`\nentry populates `snapshotMode` and a `capture.mode` entry populates `captureMode`. Every\nstream export must resolve a snapshot mode from one of those two places.","properties":{"captureMode":{"type":"string","enum":["change_streams_update_full","change_streams_update_full_with_pre_image"],"default":"change_streams_update_full","description":"How MongoDB change-stream events are materialized into records. Use\n`change_streams_update_full_with_pre_image` when downstream steps need the document's prior\nstate. MongoDB listeners only — `RDBMSExport` (SQL Server CDC, PostgreSQL) does not set this."},"snapshotMode":{"type":"string","enum":["initial","no_data","when_needed","initial_only"],"default":"no_data","description":"Whether the listener seeds existing rows before streaming changes. Use `initial` to backfill\nexisting data on the first run; `no_data` to stream only new changes."},"slotName":{"type":"string","pattern":"^[a-z_][a-z0-9_]{0,62}$","description":"Name of the PostgreSQL logical replication slot the listener consumes. PostgreSQL listeners\nonly. When omitted, the platform manages the slot name. Must be a valid PostgreSQL\nidentifier (lowercase letters, numbers, or underscores, starting with a letter or\nunderscore)."},"publicationName":{"type":"string","pattern":"^[a-z_][a-z0-9_]{0,62}$","description":"Name of the PostgreSQL publication that defines which tables stream changes. PostgreSQL\nlisteners only. When set, the publication is the sole table filter and `rdbms.tables`\nstays unset. Must be a valid PostgreSQL identifier."},"cursor":{"type":"string","description":"Server-managed stream position marker (where the listener resumes reading change events).\nMaintained by the platform as events are consumed; the UI exposes a reset workflow for it\nunder Cursor Management."},"paths":{"type":"array","items":{"type":"string"},"description":"Dot-notation paths within each raw change event to retain in the emitted record. MongoDB\nlisteners default to `[\"payload\"]` (the whole change document); SQL Server and PostgreSQL\nlisteners commonly select `payload.after`, `payload.op`, and related fields to expose the\nchanged row and operation type."},"intervalTime":{"type":"integer","default":300,"description":"Polling interval, in seconds, at which the listener checks the source for new change events."},"properties":{"type":"array","description":"Connector-engine settings passed through to the underlying CDC connector as name/value\npairs. Mirrors the form's additional-properties grid: the required keys (`capture.mode`\nand `snapshot.mode` for MongoDB, `snapshot.mode` for SQL Server and PostgreSQL) are\nserialized first, followed by optional keys such as `field.exclude.list`,\n`column.include.list`, or `snapshot.collection.filter.overrides.<db>.<collection>`.\nOn save the API also lifts `snapshot.mode` / `capture.mode` entries into the typed\n`snapshotMode` / `captureMode` fields.","items":{"type":"object","required":["name","value"],"properties":{"name":{"type":"string","description":"Connector property key (e.g. `capture.mode`, `snapshot.mode`, `field.exclude.list`)."},"value":{"type":"string","description":"Connector property value."}}}}}},"Topic":{"type":"object","description":"Subscription settings for an Event Streams listener (`adaptorType: TopicExport`). The\nlistener consumes messages published to the topic and hands them to the flow in batches;\nthe platform delivers them internally, so the export carries no `_connectionId`. Present\nwhen `adaptorType` is `TopicExport`; the server fills `startPosition` and `maxWaitTimeMs`\nwith their defaults when they are omitted.","properties":{"_topicId":{"type":"string","format":"objectId","description":"Topic this listener subscribes to. Create or look it up with `GET /v1/topics`; the\ntopic must belong to the same account. Pointing an existing listener at a different\ntopic starts a new subscription, read from `startPosition`. A clone made in the same\nenvironment — of this export, or of a flow that contains it — keeps `_topicId`, so the\ncopy becomes another listener on the topic once its flow is enabled."},"startPosition":{"type":"string","enum":["latest","earliest"],"default":"latest","description":"Where the listener starts reading when it first subscribes to `_topicId`. Use `earliest`\nto backfill retained history, `latest` (the default) to process only new messages. Read\non create and whenever `_topicId` changes; on other updates the stored value is kept even\nif a different value is sent."},"maxWaitTimeMs":{"type":"integer","minimum":1000,"default":30000,"description":"Longest time, in milliseconds, the listener waits to fill a batch before delivering\nwhatever it has collected. Lower values reduce latency for sparse topics; the default\nof 30 seconds favors fuller batches. The batch record cap is the export's `pageSize`."}}},"FileSystem":{"type":"object","description":"Defines which files to read from a local or mounted folder on the host running the\non-premise agent. Required when the _connectionId field references a file-system (on-premise)\nconnection; must not be included for other connection types.","required":["directoryPath"],"properties":{"directoryPath":{"type":"string","description":"Folder on the on-premise agent's host to read files from; the agent's OS account must\nhave read permission on it. Accepts a local OS path or a UNC network share, and supports\nhandlebars templates for dynamic folders."}}},"Http":{"type":"object","description":"Configuration for HTTP exports. Required when the export's `_connectionId` references a\nconnection of type `http`, and on `NetSuiteHTTPExport` (NetSuite's REST APIs on a `netsuite`\nconnection, where `nsDomainType` supplies the host).","properties":{"type":{"type":"string","enum":["file","blob","csvstream"],"description":"Set to `file` when the export works with files rather than a record-returning endpoint: raw file downloads (PDFs, images, binary data) and the folder mode of the cloud file providers (Google Drive, Box, Dropbox, Azure Blob, Google Cloud Storage, Celigo Storage), where each file in the folder is parsed by the `file` object's `type` (csv, json, xml, xlsx, filedefinition — including EDI) into records; the `file` object must also be configured. Leave undefined for standard data exports that return structured records (JSON, XML, GraphQL, SOAP responses) — the response is then parsed into records for downstream steps. When set to `file` or `blob`, the export appears as a \"Transfer\" step in Flow Builder instead of a standard \"Export\" step."},"formType":{"type":"string","enum":["http","rest","graph_ql","assistant","assistant_graphql"],"description":"Authoring mode of the HTTP request form. Controls which editor the UI presents\n(standard HTTP, legacy REST, GraphQL, or connector-assistant driven) and how the\nrequest fields are interpreted. When omitted, the standard HTTP form is used."},"method":{"type":"string","description":"HTTP method used to request data from the target API. Consult the target API's\ndocumentation to determine the appropriate method. `HEAD` exists for connector endpoints\nthat return metadata in response headers only (Azure Data Lake Storage Gen2 path\nproperties).","enum":["GET","POST","PUT","PATCH","DELETE","HEAD"]},"followRedirects":{"type":"boolean","default":true,"description":"When explicitly false, 3xx responses are not followed — the redirect\nresponse itself (status code, `Location` header, body) becomes the\nrecord. Omitted or true follows allowed redirects (the default\nbehavior)."},"maxRedirects":{"type":"integer","minimum":1,"maximum":10,"description":"Caps how many consecutive 3xx redirects are followed. Only applies\nwhen `followRedirects` is not false; to not follow at all, set\n`followRedirects: false` rather than `maxRedirects: 0`. Decimal\nvalues are rejected on save."},"relativeURI":{"type":"string","description":"Resource path appended to the connection's `baseURI` to form the complete request URL. Path segments, query parameters, or the entire value can be built with handlebars expressions for endpoints determined at runtime. For lookup exports (`isLookup: true`) with mappings configured, handlebars always render against the original pre-mapped input record, so fields removed or renamed by mappings remain available for URI construction. On `NetSuiteHTTPExport` the base URL is derived from the NetSuite connection and `nsDomainType`, so the path starts at `/services/rest/...` or `/app/site/hosting/restlet.nl`.\n\nLeave it empty to call the connection's `baseURI` exactly; `/` appends a trailing slash, which is a different URL. An export saved with an empty relative URI and no `http.body` comes back from reads without its `http` block, so a PUT built from such a read loses the stored settings: send the complete `http` block with every PUT."},"headers":{"type":"array","description":"Headers specific to this export, merged with (and able to override) headers defined on the connection. Define common headers such as authentication on the connection instead. Values support handlebars expressions; for lookup exports with mappings, values render against the pre-mapped input record.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header to send; a header defined here overrides a same-named header from the connection."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions; for lookup exports with mappings, the value renders against the pre-mapped input record."}}}},"requestMediaType":{"type":"string","description":"Overrides the connection-level request media type for this export. Set only when this endpoint requires a different format than the connection default.","enum":["json","xml","urlencoded","form-data","plaintext"]},"body":{"type":"string","description":"Request body sent with POST, PUT, or PATCH requests, typically carrying query or filter criteria for APIs (such as GraphQL or SOAP) that expect them in the body. The content must match the format set by `requestMediaType` and supports handlebars expressions for dynamic values."},"successMediaType":{"type":"string","description":"Media type used to parse successful response bodies. Set only when the response format differs from the request format.","enum":["json","xml","csv","plaintext"]},"errorMediaType":{"type":"string","description":"Media type used to parse error response bodies. Set only when error responses use a different format than the request.","enum":["json","xml","plaintext"]},"_asyncHelperId":{"type":"string","format":"objectId","description":"AsyncHelper resource that handles polling for long-running operations on APIs that process requests asynchronously (HTTP 202 responses, job tickets, feed or document IDs). Set when the export must submit a request, poll for status, and retrieve results once the external process completes — for example Amazon SP-API feeds or large report generators."},"once":{"type":"object","description":"Callback configuration for once exports, used to mark records as exported in the source system after successful processing.\nCalled only while the export's `type` is `once`: an export with no `type` keeps a saved `once` and never calls it, so every\nrun reads every record again (live-verified 2026-10-22).","properties":{"relativeURI":{"type":"string","description":"Relative path (starting with `/`) called on the source system to mark each record as exported after successful processing. Supports handlebars variables and renders against the pre-mapped record — mappings do not apply to the callback URI."},"method":{"type":"string","description":"HTTP method used for the mark-as-exported callback request.","enum":["GET","PUT","POST","PATCH","DELETE"]},"body":{"type":"string","description":"Request body sent with the mark-as-exported callback. Supports handlebars expressions for dynamic values."}}},"paging":{"type":"object","description":"Controls how the export requests subsequent pages when the API returns multi-page responses. The `method` field determines which companion fields are required and how each next page is requested. For the page, skip, and token methods, reference the matching pagination variable (`{{export.http.paging.page}}`, `{{export.http.paging.skip}}`, or `{{export.http.paging.token}}`) in the relative URI or request body — misconfigured pagination is a common cause of incomplete data retrieval.\n\nWhen this export is the first lookup of a builder-mode API with `pagination.enabled` set, it is that API's volume driver: saving the export (even unchanged) invalidates the API's issued cursors, so a caller mid-walk gets an empty final page, and removing `paging` is accepted on save but makes every call to the API fail with `400 api_pagination_source_not_paged` until paging is restored or the API's flag is turned off.\n\nWhen a next page's response is identical to the previous one, paging stops: the export records an open error with code `page_response_error` (\"Identical response received from next page request. No more page requests will be made.\") and the run completes with the pages already read, so later pages are never exported. A request that doesn't change between pages produces this, as observed with `method: page` and the pagination variable missing from the relative URI and body; reference the variable so each request asks for the next page.","properties":{"method":{"type":"string","description":"Pagination strategy used to request each subsequent page; match it to the mechanism documented by the target API. Determines which companion fields are required: `token` needs `path` and `pathLocation`, `url` needs `path`, `relativeuri` needs `relativeURI`, `body` needs `body`, while `linkheader` typically needs no extra configuration. Using the wrong method results in errors or incomplete data retrieval.","enum":["linkheader","page","skip","token","url","relativeuri","body"]},"page":{"type":"integer","description":"Starting page number for `method: page`. Set to 1 for APIs whose first page is not zero-indexed; when omitted, paging starts at 0. The value is incremented automatically for each subsequent page request."},"skip":{"type":"integer","description":"Starting offset for `method: skip`. Rarely needed — most APIs start at 0, and the value is incremented by the page size automatically for each subsequent request. Set only when the API requires a non-zero starting offset."},"token":{"type":"string","description":"Initial token for `method: token`. Leave empty for normal pagination — the first request is sent without a token, and subsequent tokens are extracted from each response via `path`. Set only to resume from a known token or for APIs that require a token on the first request."},"path":{"type":"string","description":"Location of the pagination value in each response — the continuation token for `method: token`, or the complete next-page URL for `method: url`. When `pathLocation` is `body`, use a dot-notation JSON path (e.g. `meta.nextToken`); when `header`, use the exact case-sensitive header name. Not used by other pagination methods."},"pathLocation":{"type":"string","description":"Where the export looks for the pagination token referenced by `path`. Required for `method: token`; not used by other pagination methods.","enum":["body","header"]},"pathAfterFirstRequest":{"type":"string","description":"Alternative token location used for responses after the first page, in the same format as `path`. Set only when the API moves the token to a different location in subsequent responses — setting it unnecessarily can cause pagination to fail."},"relativeURI":{"type":"string","description":"Overrides the main relative URI for second and later page requests; leave empty when the main relative URI works for all pages. Build it with handlebars using the `previous_page` context — `previous_page.full_response` (the prior response body), `previous_page.last_record` (the last record of the prior page), and `previous_page.headers` (the prior response headers)."},"body":{"type":"string","description":"Overrides the main request body for second and later page requests, typically for GraphQL or SOAP APIs that paginate through the body; leave empty when the main body works for all pages. Build it with handlebars using the `previous_page` context — `previous_page.full_response`, `previous_page.last_record`, and `previous_page.headers`."},"mergeBodyParamsToPagingBody":{"type":"boolean","description":"Only applies when `paging.method` is `body`. When true, the body\nparameters produced by the export's mappings at runtime are merged\ninto the evaluated `paging.body` for page 2+ requests, so paging\nrequests keep the same mapped parameters as the first page.\nOmitted/false keeps the original behavior — page 2+ requests send\nonly the evaluated `paging.body`."},"linkHeaderRelation":{"type":"string","description":"Link header relation followed for `method: linkheader` when the API uses a value other than the default `next`. Case-sensitive and must exactly match the `rel` value in the Link header, without the `rel=` prefix."},"resourcePath":{"type":"string","description":"Overrides the path to records for second and later page responses. Set only when follow-up pages place records at a different location than the first response; leave empty when all pages share the same structure."},"lastPageStatusCode":{"type":"integer","description":"HTTP status code that signals the last page, replacing the default behavior of treating 404 as the end of pagination. When this status is received (any code but `200` paired with `lastPagePath`), paging stops and the response is not treated as an error. Set it when the API uses a non-404 code (such as 204 or 400) to indicate no more pages, or to `200` together with `lastPagePath` when the last page is an ordinary 200 response: then the response is parsed, its records are kept, and paging stops only when `lastPagePath` matches `lastPageValues`."},"lastPagePath":{"type":"string","description":"JSON path to a response-body field that signals the end of pagination, such as a \"has more\" flag or a cursor that empties on the last page. Must be used with `lastPageValues`, which lists the values at this path that stop paging. If the path does not exist in a response, the condition is not considered met. With `method: token` the path is read only on a response whose status code equals `lastPageStatusCode`, so pair it with `lastPageStatusCode: 200` for an ordinary last page; with `method: body` it is read on every response."},"lastPageValues":{"type":"array","description":"Values at `lastPagePath` that stop pagination; a match on any entry ends paging. All entries are compared as exact, case-sensitive strings, even for boolean or numeric fields — use `\"true\"`, `\"false\"`, `\"null\"` for JSON null, or `\"\"` for an empty string. Required when `lastPagePath` is set.","items":{"type":"string"}},"maxPagePath":{"type":"string","description":"JSON path to the total page count in the response, used to stop paging once the last page is reached. Only applies to the `page` and `skip` methods. Point it at the total number of pages, not the current page number."},"maxCountPath":{"type":"string","description":"JSON path to the total record count in the response, used to stop paging once all records have been retrieved. Only applies to the `page` and `skip` methods; when both are set, `maxPagePath` takes precedence. Point it at the total number of records, not the count in the current page."}},"if":{"properties":{"method":{"const":"body"}},"required":["method"]},"then":{"required":["body"]},"else":{"if":{"properties":{"method":{"const":"token"}},"required":["method"]},"then":{"required":["path","pathLocation"]},"else":{"if":{"properties":{"method":{"const":"relativeuri"}},"required":["method"]},"then":{"required":["relativeURI"]},"else":{"if":{"properties":{"method":{"const":"url"}},"required":["method"]},"then":{"required":["path"]}}}}},"response":{"type":"object","description":"Controls how records are extracted from the API response and how success or failure is detected at the response level. When the API wraps records in an envelope object, set `resourcePath` to the path of the records array — without it, the entire response body is treated as a single record. Leave this object undefined when the API returns a bare JSON array.","properties":{"resourcePath":{"type":"string","description":"Dot-separated path to the array of records inside the response body (e.g. `data.customers` for `{\"data\": {\"customers\": [...]}}`); without it, a wrapped response is treated as a single record. Values starting with `$` are evaluated as JSONPath instead of dot notation — including unions (`$['orders','invoices'][*]`) and filter expressions (`$.items[?(@.qty>3)]`); all other values keep the existing dot-notation behavior unchanged. Leave undefined when the API returns a bare JSON array. This extracts records from the API response — not to be confused with `oneToMany`/`pathToMany`, which unwrap arrays from input records, or `paging.resourcePath`, which applies only to subsequent page responses."},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"},"resourceIdPath":{"type":"string","description":"Path to the unique identifier within each record, used primarily when processing results of asynchronous import responses. When omitted, the system looks for standard `id` or `_id` fields automatically."},"successPath":{"type":"string","description":"Path to a response field that signals whether the call succeeded, for APIs that return HTTP 200 even on failure. With `successValues`, the value at this path must match one of them; without `successValues`, the path only has to resolve — a response in which it is absent fails with `response_failure` (\"Unable to extract paths from response body using path: <path>, Response: …\")."},"successValues":{"type":"array","items":{"type":"string"},"description":"Values at `successPath` that mark the response as successful; any other value is treated as an error. All comparisons are string-based — use `\"true\"` or `\"false\"` for boolean fields."},"errorPath":{"type":"string","description":"Path to the error message in the response body. The value at this path is included in error logs and error records when the API returns an error; without it, the message is the whole response body."},"failPath":{"type":"string","description":"Path to a response field that signals failure even when the HTTP status code is 200 — the inverse of `successPath`. With `failValues`, the response fails when the value there matches one of them; without, when the path holds a truthy value. A path that resolves to an array matches only when the array has exactly one element."},"failValues":{"type":"array","items":{"type":"string"},"description":"Values at `failPath` that mark the response as failed, even when the HTTP status code is 200."},"allowArrayforSuccessPath":{"type":"boolean","description":"When true, treats the value at `successPath` as an array and counts the response as successful if any element matches `successValues`, rather than requiring a single scalar match. Set it for APIs that return per-record status arrays in a batch response."},"twoDArray":{"type":"object","description":"Parsing options for endpoints that return tabular data as a two-dimensional array (rows of cells) rather than an array of objects, such as spreadsheet-style or report APIs.","properties":{"hasHeader":{"type":"boolean","description":"When true, the first row is treated as column headers and used to name the fields of each generated record."},"doNotNormalize":{"type":"boolean","description":"When true, the rows are passed through as raw arrays instead of being normalized into keyed records."}}},"blobFormat":{"type":"string","description":"Controls how the binary response body is decoded for blob exports. Only relevant when `http.type` is `file` or the export type is `blob`.","enum":["utf8","ucs2","utf-16le","ascii","binary","base64","hex"]}}},"_httpConnectorVersionId":{"type":"string","format":"objectId","description":"Pins the export to a version of the published HTTP connector its connection is bound to\n(`connection.http._httpConnectorId`), so the UI renders that connector's endpoint form instead\nof a hand-authored request. Linked on save together with `_httpConnectorResourceId` and\n`_httpConnectorEndpointId` — see that field for the rule."},"_httpConnectorResourceId":{"type":"string","format":"objectId","description":"The connector resource (a group of related endpoints, such as Orders) that this export's\nendpoint belongs to. Linked on save together with `_httpConnectorEndpointId` — see that field\nfor the rule."},"sendAuthForFileDownloads":{"type":"boolean","description":"When true, includes authentication headers when downloading files."},"_httpConnectorEndpointId":{"type":"string","format":"objectId","description":"The connector endpoint this export calls, shown in the UI as the selected endpoint. On save the\nplatform derives it (with the version and resource ids) from `relativeURI` and `method` when\nthey match an endpoint of the connector the connection is bound to — path parameters, literal\nids and query strings still match — and replaces ids that belong to another connector. An id for\nthe connection's own connector is kept as sent even when the URI names a different endpoint;\nwhen nothing matches, or the connection has no connector, whatever was sent is kept and nothing\nis derived."}}},"Salesforce":{"type":"object","description":"Configuration for Salesforce exports. Required when the export's `_connectionId` references a Salesforce connection; omit for other connection types. The `type` field selects the extraction mode: `soql` runs batch queries and requires the `soql` object, while `distributed` listens for real-time events and requires the `distributed` object. File retrieval (when the export's `type` is `blob`) requires `sObjectType` and `id` instead.","properties":{"type":{"type":"string","description":"Selects the extraction mode and determines which configuration object is required. Use `soql` for scheduled or on-demand batch queries and lookups — it requires the `soql` object and works with both the `rest` and `bulk` APIs and the standard, delta, test, and once export types. Use `distributed` for real-time event handling — it requires the `distributed` object, always appears as a Listener in Flow Builder, ignores the `api` field, and supports only the standard export type.","enum":["soql","distributed"]},"sObjectType":{"type":"string","description":"API name of the Salesforce object the export operates on; the object must exist in the connected org and be accessible to the integration user. Required for distributed exports and for blob exports — blob exports accept only the file storage objects (`Attachment`, `ContentVersion`, `Document`) paired with the `id` field. Optional for SOQL exports, where the object can be inferred from the query."},"id":{"type":"string","description":"Salesforce record ID of the file to retrieve; required for blob exports and not used for `soql` or `distributed` exports. Accepts a static ID or a handlebars expression such as `{{record.Attachment_Id__c}}` to resolve the file at runtime — dynamic IDs require the export to run as a lookup (`isLookup: true`). The ID must belong to the object named in `sObjectType`."},"includeDeletedRecords":{"type":"boolean","description":"When true, SOQL exports use Salesforce's `queryAll()` instead of `query()`, including Recycle Bin records deleted within the past 15 days; each record's `IsDeleted` field identifies deletions. Useful for synchronizing deletes to target systems or maintaining a complete audit trail. Ignored for distributed and blob exports.","default":false},"api":{"type":"string","description":"Salesforce API used to run SOQL queries; ignored for distributed and blob exports. Use `rest` (the default when omitted) for smaller datasets (under 10,000 records) and for lookup exports — the Bulk API is not compatible with `isLookup: true`. Use `bulk` for large data volumes and higher throughput, optionally tuned through the `bulk` object.","enum":["rest","bulk"]},"bulk":{"type":"object","description":"Settings for Salesforce Bulk API 2.0 jobs. Only applies when `api` is `bulk` on a SOQL export; ignored otherwise.","properties":{"maxRecords":{"type":"integer","description":"Caps how many records a single Bulk API job retrieves, which helps prevent timeouts with complex queries or large records. When omitted, Salesforce's default applies. Lower values suit complex or custom objects; higher values improve throughput for simple records.","minimum":10000},"purgeJobAfterExport":{"type":"boolean","default":true,"description":"When true, deletes the Bulk API job in Salesforce after all data is retrieved, keeping the Bulk Data Load Jobs list clean — at the cost of making job details unavailable for later troubleshooting. Has no effect on the data retrieved or the success of the export."}}},"soql":{"type":"object","description":"SOQL query configuration; required when `type` is `soql` and not used otherwise. The query controls which objects, fields, and filter conditions the export retrieves, with either the REST or Bulk API.\n\nRequired when type is soql.","properties":{"query":{"type":"string","description":"SOQL statement passed directly to Salesforce that defines which objects, fields, and records the export retrieves. Supports handlebars — for delta exports, filter with `WHERE LastModifiedDate > {{lastExportDateTime}}` — and relationship queries that fetch parent and child records together. Select only the fields you need and use `ORDER BY` for consistent results across pages.","maxLength":200000}}},"distributed":{"type":"object","description":"Real-time listener configuration — the object that makes an export a listener. Set it, with `type: distributed`, when creating one; a listener saved from the integrator.io form carries it with no `type`, and the platform fires it either way. Not used on SOQL or file exports. Records reach the flow only when an Apex trigger on `sObjectType` in the connected org calls the Celigo package (`integrator_da__.RealTimeExporter.processExport()`) — no scheduling or manual execution is involved. Integration Apps create their triggers at installation; for a custom flow the customer deploys the trigger in Salesforce, starting from the code the export page shows under Required trigger.","properties":{"referencedFields":{"type":["array","null"],"description":"Fields from related objects to include in the exported record, written in dot notation (e.g. `Account.Name`, `Owner.Email`, `Custom_Lookup__r.Field__c`). Works with lookup and master-detail relationships, up to 10 unique referenced relationships per export. Referenced fields are retrieved via separate API calls, so include only fields the integration actually needs.","items":{"type":["string","null"]}},"disabled":{"type":"boolean","description":"When true, the listener stops processing events while its configuration and Salesforce triggers stay in place. Events that occur while disabled are ignored, not queued — they are not processed retroactively when re-enabled, so consider a delta export to catch up after extended pauses."},"qualifier":{"type":["string","null"],"description":"SOQL WHERE-clause condition that filters which record events the listener processes; when omitted (stored as `null`), every event for the object is processed. The Celigo Salesforce package evaluates it inside Salesforce as `SELECT Id FROM <sObjectType> WHERE (<qualifier>) AND Id IN :ids` before anything is sent, so only fields of the monitored object and SOQL comparison syntax apply — Salesforce formula functions are not part of this grammar. A deleted record is never returned by that query, so a listener with any qualifier receives no delete events; leave it unset when removals must trigger the flow."},"batchSize":{"type":["integer","null"],"description":"Controls how many event records are grouped into each real-time processing batch. Stored as `null` when not configured. Smaller batches lower latency for time-sensitive operations; larger batches improve throughput for high-volume objects. Does not limit how many records are processed in total — only how they are grouped.","minimum":4,"maximum":200},"skipExportFieldId":{"type":["string","null"],"description":"API name of a Salesforce checkbox field used to prevent infinite loops in bidirectional syncs: when the integration updates a record, this field is set so the resulting event is ignored, then cleared automatically. Required for bidirectional sync scenarios. The field must be a checkbox dedicated to integration use and updateable by the integration user."},"relatedLists":{"type":["array","null"],"description":"Child record sets to include with the parent record when it changes, one entry per related list. When omitted (stored as `null`), only the primary record is processed. Each related list adds Salesforce API calls and increases payload size.","items":{"type":"object","description":"Defines how to retrieve one type of child record related to the primary object. Configure multiple entries to retrieve different types of children.","properties":{"referencedFields":{"type":"array","description":"API names of the child object fields to retrieve; only the listed fields are included, and an empty array retrieves only the `Id` field. Include only fields the integration needs — each field adds data volume and processing time.","items":{"type":["string","null"]}},"parentField":{"type":"string","description":"API name of the lookup or master-detail field on the child object that references the parent (e.g. `AccountId`, `Parent_Object__c`) — the field's API name, not the relationship name. Used to build the query that fetches children for each parent record."},"sObjectType":{"type":"string","description":"Case-sensitive API name of the child object to retrieve (e.g. `Contact`, `Custom_Child__c`) — use the API name, not the label. The object must have a relationship field to the parent and be accessible to the connected user."},"filter":{"type":"string","description":"SOQL condition that limits which child records are included; when omitted, all related children are retrieved. Provide only the condition expression without the `WHERE` keyword — it is combined automatically with the parent relationship filter."},"orderBy":{"type":"string","description":"Sort order for the retrieved child records, as field names with optional `ASC`/`DESC` directions (e.g. `CreatedDate DESC`). Provide only the fields and directions without the `ORDER BY` keywords. When omitted, Salesforce determines the order."}},"required":["parentField","referencedFields","sObjectType"]}}}}},"if":{"properties":{"type":{"const":"distributed"}},"required":["type"]},"then":{"required":["distributed","sObjectType"]},"else":{"if":{"required":["distributed"]},"then":{},"else":{"if":{"required":["id"]},"then":{"required":["sObjectType"]},"else":{"if":{"not":{"propertyNames":{"enum":["metadata"]}}},"then":{"required":["soql","type"]}}}}},"AS2":{"type":"object","description":"Configures AS2 exports and listeners. Optional — the transport settings live on the AS2\nconnection, and an AS2 export is valid with only `file` (parse settings); set this object\nto link a Trading Partner Connector or to transfer raw files as blobs. An AS2 listener\nacts as the flow's source, receiving trading-partner transmissions in near real-time and\nhandling decryption, signature verification, and MDN generation; AS2 (Applicability\nStatement 2) transmits EDI and other data securely over HTTP/S using S/MIME encryption\nand digital signatures.","properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies the partner-specific EDI configuration —\ncommunication protocol, document schemas, mappings, validation rules, and endpoint\ndetails. Set this to link the export to all settings required for AS2 communication\nwith that partner."},"blob":{"type":"boolean","description":"When true, retrieves raw files without parsing them into structured records (rendered as\na \"Transfer\" step in the flow UI). Use only when the file contents are not needed in\nsubsequent steps — for binary files or when parsing is handled downstream. Only\navailable on AS2 and VAN exports."}},"required":[]},"DynamoDB":{"type":"object","description":"Defines how records are queried from DynamoDB tables. Required when the _connectionId field\nreferences a DynamoDB connection; must not be included for other connection types. Basic\nexports need region, method, tableName, keyConditionExpression, expressionAttributeNames,\nand expressionAttributeValues; once exports (export type \"once\") additionally need\nonceExportPartitionKey, plus onceExportSortKey for composite-key tables.","required":["region","tableName","keyConditionExpression","expressionAttributeNames","expressionAttributeValues"],"properties":{"region":{"type":"string","enum":["us-east-1","us-east-2","us-west-1","us-west-2","af-south-1","ap-east-1","ap-south-1","ap-northeast-1","ap-northeast-2","ap-northeast-3","ap-southeast-1","ap-southeast-2","ca-central-1","eu-central-1","eu-west-1","eu-west-2","eu-west-3","eu-south-1","eu-north-1","me-south-1","sa-east-1"],"description":"AWS region hosting the DynamoDB table. Must match the region where the table is\ndeployed so the integration can reach it.","default":"us-east-1"},"method":{"type":"string","enum":["query"],"description":"DynamoDB operation used to retrieve items. Only \"query\" is currently supported."},"tableName":{"type":"string","description":"Name of the DynamoDB table to query. Must exactly match an existing table (case-sensitive)."},"keyConditionExpression":{"type":"string","description":"Key condition determining which items the query retrieves. Must include a condition on\nthe partition key and may add sort-key conditions (equality, BETWEEN, begins_with).\nReference attribute names with \"#\" placeholders defined in expressionAttributeNames and\nvalues with \":\" placeholders defined in expressionAttributeValues."},"filterExpression":{"type":"string","description":"Filters query results on non-key attributes after the key condition is applied; omit to\nreturn all items matching the key condition. Uses the same \"#\" and \":\" placeholders\ndefined in expressionAttributeNames and expressionAttributeValues."},"projectionExpression":{"type":"array","items":{"type":"string"},"description":"Attributes to return from each item, reducing data transfer; omit to return all\nattributes. Each array element is one field, referenced via \"#\" placeholders defined in\nexpressionAttributeNames."},"expressionAttributeNames":{"type":"string","description":"JSON string mapping \"#\" placeholders to actual attribute names, e.g.\n{\"#pk\": \"customerId\"}. Placeholders defined here are used in keyConditionExpression,\nfilterExpression, and projectionExpression."},"expressionAttributeValues":{"type":"string","description":"JSON string mapping \":\" placeholders to comparison values, e.g. {\":status\": \"ACTIVE\"}.\nValues can be static or dynamic handlebars expressions such as {{lastExportDateTime}},\nand are referenced from keyConditionExpression and filterExpression."},"onceExportPartitionKey":{"type":"string","description":"Partition key attribute that uniquely identifies items when the export's type is \"once\".\nCeligo uses it to mark items as processed after a successful export, preventing the same\nitems from being exported again on subsequent runs."},"onceExportSortKey":{"type":"string","description":"Sort key attribute used together with onceExportPartitionKey to identify processed items\nwhen the table has a composite primary key. Omit for tables keyed by a partition key\nalone."},"pathToRecords":{"$ref":"#/components/schemas/PathToRecords"},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"}}},"PathToRecords":{"type":"string","maxLength":250,"description":"Path to the array of records inside each document or item fetched from the source; each array element is emitted as an individual record, which keeps records within the 5 MB page-size limit even when the source document exceeds it. Accepts dot notation (e.g. `data.orders`) or JSONPath for values starting with `$`, including unions (`$['orders','invoices'][*]`) and filter expressions (`$.items[?(@.qty>3)]`); a path that resolves to nothing emits zero records for that document or item. When omitted, each document or item is emitted as one record, exactly as before — HTTP and file-based exports configure this same capability through their existing `http.response.resourcePath` and `file.json`/`file.xml` `resourcePath` fields."},"FTP":{"type":"object","description":"Defines which files to retrieve from an FTP, FTPS, or SFTP server. Required when the\n_connectionId field references an FTP/SFTP connection; must not be included for other\nconnection types. directoryPath selects the folder, fileNameStartsWith/fileNameEndsWith\nfilter files by name, and backupDirectoryPath controls where files are moved after\nretrieval.","required":["directoryPath"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies partner-specific B2B settings for this export.\nWhen set, the export inherits the connector's pre-configured settings; omit to use only\nthe FTP connection details."},"directoryPath":{"type":"string","description":"Directory on the server to retrieve files from, either absolute or relative to the login\ndirectory; the FTP user must have read permission on it. Use forward slashes regardless\nof server OS — paths are case-sensitive on UNIX/Linux servers. Supports handlebars\ntemplates, e.g. archive/{{date 'YYYY-MM-DD'}}."},"fileNameStartsWith":{"type":"string","description":"Only retrieves files whose names start with this value (case-sensitive on most servers);\naccepts static text or handlebars templates. When combined with fileNameEndsWith, files\nmust match both."},"fileNameEndsWith":{"type":"string","description":"Only retrieves files whose names end with this value, commonly a file extension\n(case-sensitive on most servers). When combined with fileNameStartsWith, files must\nmatch both."},"backupDirectoryPath":{"type":"string","description":"Directory on the same server where files are moved after successful export, giving you\nan independent backup; if omitted, files are simply deleted from the source directory\nafter successful export unless `file.skipDelete` is true (Celigo also keeps its own copy\nof processed files for a set period). Supports static paths or handlebars templates."}}},"JDBC":{"type":"object","description":"Configuration object for JDBC (Java Database Connectivity) data integration exports.\n\nThis object is REQUIRED when the _connectionId field references a JDBC database connection\nand must not be included for other connection types. It defines how data is extracted\nfrom relational databases using SQL queries.\n\n**Jdbc export capabilities**\n- Execute custom SQL SELECT statements\n- Support for joins, aggregations, and functions\n- Flexible data retrieval from any accessible tables or views\n- Compatible with all major database systems\n**Critical:** WHAT BELONGS IN THIS OBJECT\n- `query` - **ALWAYS REQUIRED** - The SQL SELECT statement\n- `once` - **REQUIRED** when the export's Object Type is `\"once\"` (set _include_once: true)\n- **DO NOT** put `delta` inside this object - delta is handled via the query\n\n**Delta exports (type: \"delta\")**\nFor delta/incremental exports, do NOT populate a `delta` object inside `jdbc`.\nInstead, use `{{lastExportDateTime}}` or `{{currentExportDateTime}}` directly in the query:\n```json\n{\n  \"type\": \"delta\",\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE updatedAt > {{lastExportDateTime}}\"\n  }\n}\n```\n\n**Once exports (type: \"once\")**\nFor once exports (mark records as processed), populate `jdbc.once.query`:\n```json\n{\n  \"type\": \"once\",\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE exported = false\",\n    \"once\": {\n      \"query\": \"UPDATE customers SET exported = true WHERE id = {{record.id}}\"\n    }\n  }\n}\n```\n\n**Standard exports (type: null or not specified)**\nJust provide the query:\n```json\n{\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE status = 'ACTIVE'\"\n  }\n}\n```","required":["query"],"properties":{"formType":{"type":["string","null"],"enum":["sql","simple",null],"description":"Selects how the export's query is authored. Use `sql` for a hand-written SQL statement in\n`query`; use `simple` to build the query from the `simple` object's table/columns/filter."},"query":{"type":"string","description":"SQL SELECT statement executed to retrieve data, from simple table selections to joins and\naggregations. For delta exports, include {{lastExportDateTime}}/{{currentExportDateTime}}\nin the WHERE clause rather than configuring a separate delta object."},"once":{"type":"object","description":"**CRITICAL: REQUIRED when the export's Object Type is \"once\".**\n\nIf Object Type is \"once\", you MUST set _include_once to true (or include this object).\n\nThis object has ONLY ONE property: \"query\" (a SQL UPDATE string).\nDO NOT create any other properties like \"update\", \"table\", \"set\", \"where\", etc.\n\nCORRECT format:\n```json\n{\"query\": \"UPDATE customers SET exported=true WHERE id={{record.id}}\"}\n```\n\nWRONG format (DO NOT DO THIS):\n```json\n{\"update\": {\"table\": \"customers\", \"set\": {...}}}\n```\n","properties":{"query":{"type":"string","description":"**REQUIRED** - A SQL UPDATE statement string to mark records as processed.\n\nThis is a plain SQL UPDATE query string, NOT a structured object.\n\nThe query runs AFTER each record is successfully exported, setting a flag\nto indicate the record has been processed.\n\nFormat: \"UPDATE <table> SET <column>=<value> WHERE <id_column>={{record.<id_field>}}\"\n\nExample: \"UPDATE customers SET exported=true WHERE id={{record.id}}\"\n\nThe {{record.id}} placeholder is replaced with the actual record ID from each exported row.\n"}}},"simple":{"type":"object","description":"Visual query-builder configuration used when `formType` is `simple`; the platform builds\nthe SELECT from these instead of raw SQL. Only relevant when `formType` is `simple`.","properties":{"table":{"type":"string","description":"Table or view to select rows from."},"columns":{"type":"array","items":{"type":"string"},"description":"Columns to return; an empty list selects all columns."},"filter":{"description":"Filter applied as the WHERE clause for the generated query, stored as the\nplatform's structured filter-rules object.","allOf":[{"$ref":"#/components/schemas/Filter"}]}}}}},"MongoDB":{"type":"object","description":"Defines how documents are retrieved from MongoDB collections. Required, with `method` and\n`collection`, when the _connectionId field references a MongoDB connection and the export\nfetches documents; must not be included for other connection types. A change-stream\nlistener (`type: stream`) is configured through `cdc` instead and may omit this object, or\nset only `collection` to scope the listener. Supports find queries with optional filter\ncriteria and field projections.","properties":{"method":{"type":"string","enum":["find"],"description":"MongoDB operation used to retrieve documents. Only \"find\" is currently supported,\nequivalent to db.collection.find(filter, projection)."},"collection":{"type":"string","description":"Name of the MongoDB collection to query. Case-sensitive and must reference an existing\ncollection in the connected database. Required on find exports; on a change-stream\nlistener (`type: stream`) it is optional and may list several collections separated by\ncommas, matching the listener form's \"Collections\" field."},"filter":{"type":"string","description":"MongoDB query document, as a JSON string, that selects which documents to export; omit\nto return every document in the collection. Supports standard MongoDB query operators\nand handlebars variables for dynamic values — for example\n{\"lastModified\": {\"$gt\": \"{{lastRun}}\"}} for incremental processing."},"projection":{"type":"string","description":"MongoDB projection document, as a JSON string, that limits which fields are returned;\nomit to return all fields. Use 1 to include fields or 0 to exclude them — the two modes\ncannot be mixed except for _id, which is included by default unless explicitly excluded.\nProjection affects only the shape of returned documents, not which documents match."},"pipeline":{"type":"string","description":"MongoDB aggregation pipeline, as a JSON-array string of stages, used instead of a plain\nfind when documents need to be transformed, joined, or grouped server-side (e.g. `$match`,\n`$lookup`, `$group`). When set, it takes the place of `filter`/`projection`. Supports\nhandlebars variables for incremental processing."},"readPreference":{"type":"string","enum":["doNotOverride","primary","primaryPreferred","secondary","secondaryPreferred","nearest"],"default":"doNotOverride","description":"Overrides which replica-set member this export reads from, trading consistency for load\ndistribution. Leave at `doNotOverride` to inherit the connection's setting."},"pathToRecords":{"$ref":"#/components/schemas/PathToRecords"},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"}}},"NetSuite":{"type":"object","description":"NetSuite-specific export configuration. Required when `_connectionId` references a NetSuite\nconnection; omit for all other connection types. Supports saved-search, RESTlet, and\ndistributed (SuiteApp) exports, plus two file cabinet modes: blob exports transfer files\nas-is (set the export's top-level `type` to `blob` and configure `netsuite.blob`), while\nfile exports parse file contents into records (leave the export's top-level `type` unset\nand configure `netsuite.file`).","properties":{"type":{"type":"string","enum":["search","basicSearch","metadata","selectoption","restlet","getList","getServerTime","distributed","file"],"description":"Controls how data is retrieved from NetSuite and which sibling object must be configured:\n`search` pairs with `netsuite.searches`, `restlet` with `netsuite.restlet`, `distributed`\nwith `netsuite.distributed`, and `file` with `netsuite.file.folderInternalId`. For blob\nexports (raw file transfer without parsing), leave this field unset, set the export's\ntop-level `type` to `blob`, and configure `netsuite.internalId` instead. For lookups\n(`isLookup: true`), prefer `restlet`, which supports `suiteapp2.0` saved searches with\ndynamic inputs; `search` is limited for dynamic lookups."},"searches":{"type":"array","description":"Saved search configurations that query NetSuite for records. Each entry references a\nsaved search by internal ID, names the record type being searched, and can add filter\ncriteria.","items":{"type":"object","properties":{"savedSearchId":{"type":"string","description":"Internal ID of the NetSuite saved search to run."},"recordType":{"type":"string","description":"NetSuite record type being searched, as its exact lowercase script ID\n(e.g. \"customer\", \"salesorder\") — not the display name."},"criteria":{"type":"array","description":"Additional filter criteria applied on top of the saved search.","items":{"type":"object","properties":{"field":{"type":"string","description":"Script-id of the search column to filter on.  REQUIRED.\nMust come from the record type's SEARCH COLUMNS list\n(NOT the body-fields list).  Becomes the `name`\nargument of NetSuite's `search.createFilter()`."},"operator":{"type":"string","description":"Comparison operator.  REQUIRED.  Supported values\ndepend on the column's data type (e.g. `is`,\n`isnot`, `after`, `before`, `anyof`,\n`noneof`, `contains`, `startswith`,\n`greaterthan`, `lessthan`).  Must be a string\nNetSuite recognises -- mismatched operators cause\nruntime rejection."},"join":{"type":"string","description":"Optional join name when filtering through a related\nrecord (e.g. `customer` from a sales order).  Use\nonly when the column lives on a related record and\nNetSuite supports the join from the base record type."},"searchValue":{"description":"Value (or array of values for `anyof`-style\noperators) to compare against the column.  Required\nfor most operators; omit for the few unary operators\n(e.g. `isempty`, `isnotempty`)."}},"required":["field","operator"]}}}}},"metadata":{"type":"object","description":"Supplemental metadata associated with the NetSuite export."},"selectoption":{"type":"object","description":"Configuration for `selectoption` exports, which retrieve the available select options\nfor a NetSuite field."},"customFieldMetadata":{"type":"object","description":"Metadata describing the custom fields defined in the NetSuite account."},"skipGrouping":{"type":"boolean","description":"When true, each saved-search result row becomes its own record. When false or omitted,\nconsecutive result rows that share the record's internal id are grouped into one record\nshaped as an array of rows — the *Group rows* option in the UI."},"statsOnly":{"type":"boolean","description":"When true, returns only summary statistics about matching records instead of the\ndetailed records themselves."},"internalId":{"type":"string","description":"Internal ID of the file in the NetSuite file cabinet to export. Required for blob\nexports (the export's top-level `type` is `blob`); the file is transferred as-is\nwithout parsing. For parsed file exports, use `netsuite.file.folderInternalId` instead."},"blob":{"type":"object","properties":{"purgeFileAfterExport":{"type":"boolean","description":"When true, permanently deletes the file from the NetSuite file cabinet after a\nsuccessful export. When false or omitted, the file is left in place."}},"description":"Configuration for blob exports, which transfer files from the NetSuite file cabinet\nas-is without parsing them into records. To export a blob, set the export's top-level\n`type` to `blob`, set `netsuite.internalId` to the file's internal ID, and leave\n`netsuite.type` unset. Use `netsuite.file` instead when file contents should be parsed\ninto records."},"restlet":{"type":"object","properties":{"recordType":{"type":"string","description":"NetSuite record type the RESTlet operates on, as its script ID. Custom record\ntypes use the \"customrecord_\" prefix."},"batchSize":{"type":"number","description":"Number of records the RESTlet returns per request (default 1000). This, not the\nexport's `pageSize`, sizes each NetSuite call; the export keeps requesting until the\nsearch is exhausted. Larger batches reduce round-trips but raise NetSuite governance\nand memory use per call; tune to the record's size. The RESTlet caps a call at 1000,\nso a larger value acts as 1000. In a test run or preview the request is capped at the\nexport's `pageSize` (default 20) instead."},"searchId":{"type":["string","null"],"description":"Internal ID of the NetSuite saved search the RESTlet executes. Results follow the\nsaved search's current definition, so editing the search in NetSuite changes the\nexport output without changing this ID."},"useSS2Restlets":{"type":"boolean","description":"When true, calls SuiteScript 2.0 RESTlets. When false or omitted, legacy\nSuiteScript 1.0 RESTlets are used.\nSet at step creation together with restletVersion — the same\ncreation-time-only constraint applies (see restletVersion)."},"restletVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"RESTlet version the export invokes. Defaults to `suiteapp2.0` when\n`useSS2Restlets` is true, `suitebundle` otherwise.\nThe version is fixed when the step is created — the Advanced selector is\ndisabled on existing steps, and migrating an existing export to a\ndifferent version means recreating or cloning it."},"criteria":{"type":"array","description":"Filter conditions added on top of the saved search (the UI's \"Additional search\ncriteria\"). Each condition pairs a search column with an operator and one or two\ncomparison values, and the conditions are combined with AND — every one must match.","items":{"type":"object","properties":{"field":{"type":"string","description":"Script-id of the search column to filter on. This is the primary\nfield the criterion is evaluated against (e.g. \"trandate\", \"status\")."},"join":{"type":"string","description":"Join relationship that applies this filter to a related record's fields instead\nof the base record. Must match a join name NetSuite defines for the record type."},"operator":{"type":"string","enum":["after","allof","any","anyof","before","between","contains","doesnotcontain","doesnotstartwith","equalto","greaterthan","greaterthanorequalto","haskeywords","is","isempty","isnot","isnotempty","lessthan","lessthanorequalto","noneof","notafter","notallof","notbefore","notbetween","notequalto","notgreaterthan","notlessthan","notlessthanorequalto","noton","notonorafter","notonorbefore","notwithin","on","onorafter","onorbefore","startswith","within"],"description":"Comparison operator applied between the column and the search value, using\nNetSuite's lowercase search operator names. Which operators a column accepts\ndepends on its data type."},"searchValue":{"description":"Value the column is compared against — a string, a number, or an array of\nvalues for `anyof`-style operators. On lookup steps it may be a Handlebars\nexpression rendered from the incoming record (for example `{{poNumber}}`)."},"searchValue2":{"description":"Second comparison value for the two-value operators `between` and `notbetween`\n(the upper bound). Omit for every other operator."},"formula":{"type":"string","description":"NetSuite formula expression used as the filter condition, for criteria that\nstandard field-operator-value comparisons can't express."}}}},"columns":{"type":"array","description":"Search columns the RESTlet returns for each matching record. With a saved search,\nthe RESTlet returns the search's own result columns followed by any listed here: a\nlist adds columns and never removes one of the search's, so remove a column in the\nsaved search itself. An ad-hoc search (no `searchId`) must list its columns here.","items":{"type":"object","properties":{"_id":{"type":"string","readOnly":true,"description":"Subdocument id the server assigns to the column entry on save. Not a NetSuite\nfield id — never set it when authoring."},"name":{"type":"string","description":"Column field name as referenced in the search."},"join":{"type":"string","description":"Join relationship that sources this column from a related record instead of\nthe base record. Supports dot notation for nested joins (e.g. \"employee.manager\")."},"summary":{"type":"string","enum":["group","sum","count","avg","min","max"],"description":"Summary type applied to this column, turning the search into a summary search."},"formula":{"type":"string","description":"NetSuite formula expression that computes this column's value at runtime, such\nas a CASE statement or date formatting, instead of reading a stored field."},"label":{"type":"string","description":"Display name for the column; it becomes the property name of the column's value\nin each exported record."},"sort":{"type":"boolean","description":"When true, query results are sorted by this column's values."}}}},"markExportedBatchSize":{"type":"integer","description":"Number of records updated per RESTlet call when marking records as exported in\nNetSuite (live: an integer count, e.g. 100).","maximum":100,"minimum":1},"hooks":{"type":["object","null"],"properties":{"batchSize":{"type":"number","description":"Legacy location of the RESTlet page size; `restlet.batchSize` is the current\nfield and takes precedence when both are set."},"preSend":{"type":"object","properties":{"fileInternalId":{"type":"string","description":"Internal ID of the JavaScript file in the NetSuite File Cabinet that defines\nthe hook function. Must be readable by the role behind the export's connection."},"function":{"type":"string","description":"Name of the global function in that file that NetSuite invokes as the pre\nsend hook."},"configuration":{"type":"object","description":"Free-form JSON stored with the hook reference for the script's own use. Custom\nsettings in scope for the export are passed to the function separately as\n`options.settings`."}},"description":"SuiteScript hook that runs inside NetSuite after a page of records is collected\nand before it is sent to the Celigo platform."}},"description":"SuiteScript hooks for the export. Only `preSend` exists on exports; imports carry\nthe pre map, post map, and post submit hooks."},"cLocked":{"type":"object","description":"Lock state that prevents modification of the configuration when set."}},"description":"Configuration for `restlet` exports. Identifies the RESTlet script that retrieves data\nfrom NetSuite, along with the saved search, criteria, and columns it executes with."},"distributed":{"type":"object","properties":{"recordType":{"type":"string","description":"NetSuite record type the listener is deployed on, as its lowercase script ID\n(`customer`, `salesorder`, `customrecord_<scriptid>`) — never the display name or a\ncustom record's numeric internal ID. Types the platform's record-type catalog marks\n`doesNotSupportRTExport` cannot host a listener and are not offered in the UI."},"executionContext":{"type":"array","description":"NetSuite execution contexts that trigger this distributed export. A record change\nfires the export only when it occurs in one of the listed contexts.","default":["userinterface","webstore"],"items":{"type":"string","enum":["userinterface","webservices","csvimport","offlineclient","portlet","scheduled","suitelet","custommassupdate","workflow","webstore","userevent","mapreduce","restlet","webapplication","restwebservices"]}},"disabled":{"type":"boolean","description":"When true, the listener's NetSuite deployment is inactive and record saves no longer\ntrigger the export. Maintained by the platform as the flow is disabled and re-enabled;\nre-enabling the flow re-registers the deployment. A listener is created disabled, and\na flow created already enabled leaves it that way until a flow update sets\n`disabled: false`, which activates it within seconds."},"executionType":{"type":"array","description":"Record operations that trigger this distributed export. A record event fires the\nexport only when its operation matches one of the listed types.","default":["create","edit","xedit"],"items":{"type":"string","enum":["create","edit","delete","xedit","copy","view","cancel","approve","reject","pack","ship","markcomplete","reassign","editforecast","dropship","specialorder","orderitems","paybills","print","email"]}},"qualifier":{"type":["array","string","null"],"description":"Field-specific qualification criteria the SuiteApp evaluates against the record before\nexporting it; saves that do not match are discarded. Stored as a filter-expression array\nof `[field, operator, value]` rules joined by lowercase `\"and\"` / `\"or\"`, as a legacy\nexpression string, or null when no criteria are set."},"skipExportFieldId":{"type":"string","description":"Script ID of a checkbox body field on the record type. A save with that box checked is\nskipped by the listener, which then clears the box itself — used to keep a mass update\nfrom fanning out and to stop a write-back import from re-triggering the listener."},"hooks":{"type":"object","description":"SuiteScript hook that runs inside NetSuite before the record leaves it.","properties":{"preSend":{"type":"object","description":"Pre-send hook: a SuiteScript function in the NetSuite file cabinet invoked before\nrecords leave NetSuite, with the full SuiteScript API available — for example to run\ndynamic searches against the record being exported.","properties":{"fileInternalId":{"type":"string","description":"Internal ID of the file cabinet file that holds the hook's SuiteScript."},"function":{"type":"string","description":"Name of the function in that file to invoke for each export."},"configuration":{"type":"object","description":"Settings passed to the hook function."}}}}},"sublists":{"type":["array","null"],"description":"IDs of the NetSuite sublists to include with each exported record (for example `item`\nfor transaction line items). Records are exported with body fields only when no\nsublists are selected. Legacy documents may store null (equivalent to no sublists).","items":{"type":"string"}},"referencedFields":{"type":"object","description":"Field references recorded on the listener by the platform. Not exposed in the listener\nform; leave as stored."},"relatedLists":{"type":"object","description":"Related record lists recorded on the listener by the platform. Not exposed in the\nlistener form; leave as stored."},"forceReload":{"type":"boolean","description":"When true, the listener reloads the record from the database after the save before\nexporting it, so fields that are populated only once the save completes — such as\ntransaction line IDs on new lines — are included. The reload slows every save of that\nrecord type."},"ioEnvironment":{"type":"string","description":"integrator.io environment the listener's NetSuite deployment reports to. Written by the\nplatform when the listener registers with NetSuite; live-observed values include\n`production` and `staging`."},"ioDomain":{"type":"string","description":"integrator.io domain the listener's NetSuite deployment calls back to — the account's\nown regional domain, stamped by the platform on every save and echoed on create and\nupdate responses; not returned by GET."},"lastSyncedDate":{"type":"string","format":"date-time","description":"When the listener's deployment last synchronized with NetSuite. Written by the platform;\nnot set until the first successful synchronization."},"settings":{"type":"object","description":"Additional key-value settings for the distributed export. Not exposed in the listener\nform."},"useSS2Framework":{"type":"boolean","description":"Derived by the platform from `frameworkVersion` — true when it is `suiteapp2.0`, absent\notherwise — and echoed on create and update responses; not returned by GET. A value\nsent on create or update is replaced by the derived one."},"frameworkVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"SuiteApp framework version used by the distributed export.\nFixed when the step is created — the Advanced selector is disabled on\nexisting steps, and migrating an existing listener to a different\nversion means recreating or cloning it."}},"description":"Configuration for `distributed` exports, which use the NetSuite SuiteApp to fire\nreal-time, event-driven exports when records change. Define the record type to listen\nto and the execution contexts and operations that trigger it."},"getList":{"type":"array","description":"Configuration for `getList` exports — a list of record references, each retrieving one\nNetSuite record by its identifier. The export returns the fetched records in list order.","items":{"type":"object","properties":{"type":{"type":"string","description":"Standard NetSuite record type to retrieve (e.g. \"customer\", \"salesOrder\").\nUsed when `typeId` is absent. Case-sensitive."},"typeId":{"type":"string","description":"Script ID of a custom record type (\"customrecord_...\") or custom transaction\ntype (\"customtransaction_...\"). Takes precedence over `type` when present.\nCase-sensitive."},"internalId":{"type":"string","description":"Internal ID of the NetSuite record to retrieve. Assigned by NetSuite at record\ncreation and unique within the record type. Takes precedence over `externalId`."},"externalId":{"type":"string","description":"Identifier assigned by an external system, used to reference and synchronize the\nrecord across systems. Distinct from the NetSuite-assigned internal ID."}}}},"searchPreferences":{"type":"object","properties":{"bodyFieldsOnly":{"type":"boolean","description":"When true, search results include only the record's body fields and exclude fields\nfrom joined or related records. Reduces payload size when related data isn't needed."},"pageSize":{"type":"number","description":"Number of search results NetSuite returns per page."},"returnSearchColumns":{"type":"boolean","description":"When true, search results include the column data defined in the search. When\nfalse, column data is omitted and results contain only minimal record information."}},"description":"Preferences that control how NetSuite executes searches and shapes the results it returns."},"file":{"type":"object","description":"Configuration for file exports, which retrieve files from the NetSuite file cabinet\nand parse their contents (CSV, XML, JSON) into records. Leave the export's top-level\n`type` unset when using this object; for raw transfers without parsing, use\n`netsuite.blob` with the export's `type` set to `blob` instead.","properties":{"folderInternalId":{"type":"string","description":"Internal ID of the NetSuite file cabinet folder to export files from. Accepts a\nhandlebars expression (e.g. `{{record.folderId}}`) when the folder must be\nselected dynamically from record data. The ID is stable across folder renames and\nmoves, but may differ between non-production and production accounts."},"backupFolderInternalId":{"type":"string","description":"Internal ID of the NetSuite file cabinet folder where backup files are stored.\nIDs are account-specific and do not transfer between non-production and production\nenvironments."},"fileNameStartsWith":{"type":"string","description":"Optional prefix filter. Only files whose names start with this string are\nexported from the configured folder."},"fileNameEndsWith":{"type":"string","description":"Optional suffix filter. Only files whose names end with this string (e.g. a\n\".csv\" extension) are exported from the configured folder."}}}},"required":[]},"RDBMS":{"type":"object","description":"Configuration object for Relational Database Management System (RDBMS) data integration exports.\n\nThis object defines how data is read from a relational database and must not be included\nfor other connection types. For query-type exports (standard, delta, once) it is REQUIRED\nwhen the _connectionId field references an RDBMS database connection and holds the SQL\nquery. For real-time stream listeners (`type: \"stream\"`) it holds the CDC watch list\n(`tables`) instead — and a PostgreSQL listener scoped by a publication\n(`cdc.publicationName`) may omit this object entirely.\n\n**Rdbms export capabilities**\n- Execute custom SQL SELECT statements\n- Support for joins, aggregations, and functions\n- Flexible data retrieval from any accessible tables or views\n- Compatible with all major database systems\n\n**Critical:** WHAT BELONGS IN THIS OBJECT\n- `query` - The SQL SELECT statement - required for every query-type export (standard, delta, once)\n- `once` - **REQUIRED** when the export's Object Type is `\"once\"` (set _include_once: true)\n- `tables` - the CDC listener watch list - real-time stream listeners (`type: \"stream\"`) only\n- **DO NOT** put `delta` inside this object - delta is handled via the query\n- **DO NOT** author `query` on a stream listener - real-time CDC exports have no SQL query\n\n**Delta exports (type: \"delta\")**\nFor delta/incremental exports, do NOT populate a `delta` object inside `rdbms`.\nInstead, use `{{lastExportDateTime}}` or `{{currentExportDateTime}}` directly in the query, and\nset the export's top-level `delta.startDate`: without it the first run's window is empty.\n```json\n{\n  \"type\": \"delta\",\n  \"delta\": { \"startDate\": \"2026-01-01T00:00:00.000Z\" },\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE updatedAt > {{lastExportDateTime}}\"\n  }\n}\n```\n\n**Once exports (type: \"once\")**\nFor once exports (mark records as processed), populate `rdbms.once.query`:\n```json\n{\n  \"type\": \"once\",\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE exported = false\",\n    \"once\": {\n      \"query\": \"UPDATE customers SET exported = true WHERE id = {{record.id}}\"\n    }\n  }\n}\n```\n\n**Standard exports (type: null or not specified)**\nJust provide the query:\n```json\n{\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE status = 'ACTIVE'\"\n  }\n}\n```\n\n**Real-time CDC listener exports (type: \"stream\")**\nSQL Server and PostgreSQL connections support change-data-capture listeners that stream row\nchanges continuously (the `cdc` object holds the listener settings). A stream export has NO\n`query` — it watches `tables`:\n```json\n{\n  \"type\": \"stream\",\n  \"rdbms\": { \"tables\": \"public.orders,public.customers\" },\n  \"cdc\": { \"properties\": [{ \"name\": \"snapshot.mode\", \"value\": \"no_data\" }] }\n}\n```\nA PostgreSQL listener can instead scope tables through a publication\n(`cdc.publicationName`), in which case `tables` stays unset and the `rdbms` object may be\nomitted entirely.","properties":{"tables":{"type":"string","description":"Comma-separated list of tables the CDC listener watches for changes. Used only by real-time\nlistener exports (`type` is `stream`); standard SQL exports use `query` instead. SQL Server\nlisteners use fully-qualified three-part names (`database.schema.table`); PostgreSQL\nlisteners use two-part names (`schema.table`)."},"query":{"type":"string","description":"SQL SELECT statement executed to retrieve data, from simple table selections to joins,\naggregations, and parameterized queries with Handlebars expressions. Query-type exports\nonly — real-time stream listeners (`type` is `stream`) have no SQL query. For delta exports,\nreference {{lastExportDateTime}}/{{currentExportDateTime}} directly in the query rather\nthan configuring a separate delta object.\n\nDouble braces render a string as a quoted SQL literal with its apostrophes escaped, and\ntriple braces insert a value raw. Observed in Snowflake queries:\n\n- String helper output (`dateFormat`, `jsonSerialize`) and both delta variables are\n  quoted the same way; a number or boolean renders bare, whether it is a JSON value or a\n  numeric helper's result (`{{add 2 3}}` renders `5`).\n- In a run, {{lastExportDateTime}} and {{currentExportDateTime}} arrive as quoted ISO 8601\n  UTC literals with milliseconds (`'2026-01-15T00:00:00.000Z'`), or in `delta.dateFormat`\n  when it is set: add no quotes of your own, and don't parse them with a format mask\n  written for another format.\n- A preview of an unsaved export document renders both as empty, unquoted text, so a\n  delta query fails to compile there unless the document carries both in `postData`.\n- The quoting leaves backslashes, which Snowflake reads as escape sequences, so a\n  double-brace value holding a backslash arrives changed. Pass such a value in a\n  dollar-quoted constant with triple braces (`$${{{record.field}}}$$`), which Snowflake\n  reads as is, but only when it can't contain `$$`: a `$$` ends the constant, and the rest\n  is read as SQL."},"once":{"type":"object","description":"**CRITICAL: REQUIRED when the export's Object Type is \"once\".**\n\nIf Object Type is \"once\", you MUST set _include_once to true (or include this object).\n\nThis object has ONLY ONE property: \"query\" (a SQL UPDATE string).\nDO NOT create any other properties like \"update\", \"table\", \"set\", \"where\", etc.\n\nCORRECT format:\n```json\n{\"query\": \"UPDATE customers SET exported=true WHERE id={{record.id}}\"}\n```\n\nWRONG format (DO NOT DO THIS):\n```json\n{\"update\": {\"table\": \"customers\", \"set\": {...}}}\n```\n","properties":{"query":{"type":"string","description":"**REQUIRED** - A SQL UPDATE statement string to mark records as processed.\n\nThis is a plain SQL UPDATE query string, NOT a structured object.\n\nThe query runs AFTER each record is successfully exported, setting a flag\nto indicate the record has been processed.\n\nFormat: \"UPDATE <table> SET <column>=<value> WHERE <id_column>={{record.<id_field>}}\"\n\nExample: \"UPDATE customers SET exported=true WHERE id={{record.id}}\"\n\nThe {{record.id}} placeholder is replaced with the actual record ID from each exported row.\n"}}}}},"S3":{"type":"object","description":"Defines which files to retrieve from an Amazon S3 bucket. Required when the _connectionId\nfield references an AWS S3 connection; must not be included for other connection types.\nregion and bucket locate the source, keyStartsWith/keyEndsWith filter objects by key, and\nbackupBucket with keyPrefix controls where files are moved after retrieval.","required":["region","bucket"],"properties":{"region":{"type":"string","default":"us-east-1","description":"AWS region where the bucket is located. Case-insensitive; the value is normalized to\nlowercase."},"bucket":{"type":"string","description":"S3 bucket to retrieve files from. The connection's AWS credentials must have\ns3:ListBucket and s3:GetObject permissions on it."},"keyStartsWith":{"type":"string","description":"Only retrieves objects whose keys start with this value (case-sensitive), effectively\nselecting a folder in S3's flat key structure. When combined with keyEndsWith, objects\nmust match both."},"keyEndsWith":{"type":"string","description":"Only retrieves objects whose keys end with this value (case-sensitive), commonly a file\nextension. When combined with keyStartsWith, objects must match both."},"backupBucket":{"type":"string","description":"Bucket in the same region where files are moved after successful export, giving you an\nindependent backup; if omitted, files are simply deleted from the source bucket after\nsuccessful export unless `file.skipDelete` is true (Celigo also keeps its own copy of\nprocessed files for a set period). The connection's AWS credentials must have\ns3:PutObject permission on this bucket."},"keyPrefix":{"type":"string","description":"Prefix prepended to each file's name when it is moved to the backup bucket; accepts\nstatic text or handlebars templates. The original directory structure is not preserved —\nonly the filename is appended to this prefix. Ignored unless backupBucket is set."}}},"Wrapper":{"type":"object","description":"Configuration for Wrapper exports, which delegate data retrieval to custom connector code\n(typically a stack-hosted function) rather than a built-in adaptor. Required when the\n_connectionId field references a wrapper connection.","required":["function"],"properties":{"function":{"type":"string","description":"Name of the function the wrapper invokes to retrieve records. Must match a callable\nfunction in the wrapper's execution context; names are case-sensitive."},"configuration":{"oneOf":[{"type":"object","additionalProperties":true},{"type":"array","maxItems":0}],"description":"Free-form settings passed to the wrapper function at runtime (connector-specific keys such\nas method, apiVersion, headers, relativePath, or body). Structure is defined by the wrapper\ncode, not by this schema. May be stored as an empty array when no settings exist."}}},"Parsers":{"type":"array","description":"Configuration for parsing XML payloads (i.e. files, HTTP responses, etc.). Use this field when you need to process XML data\nand transform it into a JSON records.\n\n**Implementation notes**\n\n- This is where you configure how to parse XML data in your resource\n- Although defined as an array, you typically only need a single parser configuration\n- Currently only XML parsing is supported\n- Only configure this field when working with XML data that needs structured parsing\n","items":{"type":"object","properties":{"version":{"type":"string","description":"Version identifier for the parser configuration format. Currently only version \"1\" is supported.\n\nAlways set this field to \"1\" as it's the only supported version at this time.\n","enum":["1"]},"type":{"type":"string","description":"Defines the type of parser to use. Currently only \"xml\" is supported.\n\nWhile the system is designed to potentially support multiple parser types in the future,\nat this time only XML parsing is implemented, so this field must be set to \"xml\".\n","enum":["xml"]},"name":{"type":"string","description":"Optional identifier for the parser configuration. This field is primarily for documentation\npurposes and is not functionally used by the system.\n\nThis field can be omitted in most cases as it's not required for parser functionality.\n"},"rules":{"type":"object","description":"Configuration rules that determine how XML data is parsed and converted to JSON.\nThese settings control the structure and format of the resulting JSON records.\n\n**Parsing options**\n\nThere are two main parsing strategies available:\n- **Automatic parsing**: Simple but produces more complex output\n- **Custom parsing**: More control over the resulting JSON structure\n","properties":{"V0_json":{"type":"boolean","description":"Controls the XML parsing strategy.\n\n- When set to **true** (Automatic): XML data is automatically converted to JSON without\n  additional configuration. This is simpler to set up but typically produces more complex\n  and deeply nested JSON that may be harder to work with.\n\n- When set to **false** (Custom): Gives you more control over how the XML is converted to JSON.\n  This requires additional configuration (like listNodes) but produces cleaner, more\n  predictable JSON output.\n\nMost implementations use the Custom approach (false) for better control over the output format.\n"},"listNodes":{"type":"array","description":"Paths of elements that always parse as arrays. An element that can repeat parses as an\nobject when it occurs once and as an array when it occurs more than once, so list every\nrepeating element here to give each record the same shape regardless of item count.\nEach entry is the element's full path from the record element selected by the resource\npath (from the document root when there is no resource path), with an optional leading\nslash. Only relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"includeNodes":{"type":"array","description":"Limits which XML nodes are included in the output JSON.\n\nFor large XML documents, you can use this field to extract only the nodes you need,\nreducing the size and complexity of the resulting JSON. Only nodes specified here\n(and their children) will be included in the output.\n\nEach entry should be a simplified XPath expression pointing to nodes to include.\n\nOnly relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"excludeNodes":{"type":"array","description":"Specifies which XML nodes should be excluded from the output JSON.\n\nSometimes it's easier to specify which nodes to exclude rather than which to include.\nUse this field to identify nodes that should be omitted from the output JSON.\n\nEach entry should be a simplified XPath expression pointing to nodes to exclude.\n\nOnly relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"stripNewLineChars":{"type":"boolean","description":"Controls whether newline characters are removed from text values.\n\nWhen set to true, all newline characters (\\n, \\r, etc.) will be removed from\ntext content in the XML before conversion to JSON.\n","default":false},"trimSpaces":{"type":"boolean","description":"Controls whether leading and trailing whitespace is trimmed from text values.\n\nWhen set to true, all values will have leading and trailing whitespace removed\nbefore conversion to JSON.\n","default":false},"attributePrefix":{"type":"string","description":"Specifies a character sequence to prepend to XML attribute names when converted to JSON properties.\n\nIn XML, both elements and attributes can exist at the same level, but in JSON this distinction is lost.\nTo maintain the distinction between element data and attribute data in the resulting JSON, this prefix\nis added to attribute names during conversion.\n\nFor example, with attributePrefix set to \"Att-\" and an XML element like:\n```xml\n<product id=\"123\">Laptop</product>\n```\n\nThe resulting JSON would be:\n```json\n{\n  \"product\": \"Laptop\",\n  \"Att-id\": \"123\"\n}\n```\n\nThis helps maintain the distinction between element content and attribute values in the\nconverted JSON, making it easier to reference specific data in downstream processing steps.\n"},"textNodeName":{"type":"string","description":"Specifies the property name to use for element text content when an element has both\ntext content and child elements or attributes.\n\nWhen an XML element contains both text content and other nested elements or attributes,\nthis field determines what property name will hold the text content in the resulting JSON.\n\nFor example, with textNodeName set to \"value\" and an XML element like:\n```xml\n<item id=\"123\">\n  Laptop\n  <category>Electronics</category>\n</item>\n```\n\nThe resulting JSON would be:\n```json\n{\n  \"item\": {\n    \"value\": \"Laptop\",\n    \"category\": \"Electronics\",\n    \"id\": \"123\"\n  }\n}\n```\n\nThis allows for unambiguous parsing of complex XML structures that mix text content with\nchild elements. Choose a name that's unlikely to conflict with actual element names in your XML.\n"}}}}}},"Mappings":{"type":"array","description":"Array of field mapping configurations for transforming data from one format into another.\n\n**Guidance**\n\nThis schema is designed around RECURSION as its core architectural principle. Understanding this recursive\nnature is essential for building effective mappings:\n\n1. The schema is self-referential by design - a mapping can contain nested mappings of the same structure\n2. Complex data structures (nested objects and arrays of objects, to any depth) are ALL\n   handled through this recursive pattern; an array of arrays is not (build one in a script hook)\n3. Each mapping handles one level of the data structure; deeper levels are handled by nested mappings\n\nWhen generating mappings programmatically:\n- For simple fields (string, number, boolean): Create single mapping objects\n- For objects: Create a parent mapping with nested 'mappings' array containing child field mappings\n- For arrays: Use 'buildArrayHelper' with extract paths defining array inputs and\n  recursive 'mappings' to define object structures\n\nThe system will process these nested structures recursively during runtime, ensuring proper construction\nof complex hierarchical data while maintaining excellent performance.\n\nOn save, a leaf row with neither a source (`extract`, `hardCodedValue`, `lookupName`) nor a\ndestination (`generate`) is dropped silently, at every nesting level, and so is an object or\nobject-array row left with no source, no destination and no remaining child `mappings` or\n`buildArrayHelper` entries; a row with only one of the two, and a source-less, destination-less\nwrapper that still has children, are kept.\n\nThe shape of the array is the mapper's **JSON structure**. Named top-level items produce an object\nrecord (`record { }`). Exactly one top-level item with `dataType: objectarray` and no `generate` is\nthe `rows [ ]` structure: the mapped record is an array — usually one element per element of the\nsource array that root's `buildArrayHelper` iterates — Handlebars templates such as `http.body`\nbind it as `rows` instead of `record`, and the mapping editor shows that row's destination disabled.\n","items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}}},"items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}},"Transform":{"type":"object","description":"Configuration for transforming data during processing operations. This object enables\nreshaping of records.\n\n**Transformation capabilities**\n\nCeligo's transformation engine offers powerful features for data manipulation:\n- Precise field mapping with JSONPath expressions\n- Support for any level of nested arrays\n- Formula-based field value generation\n- Dynamic references to flow and integration settings\n\n**Implementation approaches**\n\nThere are two distinct transformation mechanisms available:\n\n**Rule-Based Transformation (`type: \"expression\"`)**\n- **Best For**: Most transformation scenarios from simple to complex\n- **Capabilities**: Field mapping, formula calculations, lookups, nested data handling\n- **Advantages**: Visual configuration, no coding required, intuitive interface\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear mapping requirements or need to reshape data structure\n\n**Script-Based Transformation (`type: \"script\"`)**\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Capabilities**: Full programmatic control, custom processing, complex business rules\n- **Advantages**: Maximum flexibility, can implement any transformation logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Visual transformation tools aren't sufficient for your use case\n","properties":{"type":{"type":"string","description":"Determines which transformation mechanism to use. This choice affects which properties\nmust be configured and how transformation logic is implemented.\n\n**Available types**\n\n**Rule-Based Transformation (`\"expression\"`)**\n- **Required Config**: The `expression` object with mapping definitions\n- **Behavior**: Applies declarative rules to reshape data\n- **Best For**: Most transformation scenarios from simple to complex\n- **Advantages**: Visual configuration, no coding required\n\n**Script-Based Transformation (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to transform data\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard data transformations, use `\"expression\"`\n2. For complex logic or specialized processing, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based transformations. This object enables reshaping data\nwithout requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define transformation rules that\ncan map, modify, and generate data elements.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Field mappings define how input data is transformed to target fields\n- Formulas can be used to calculate or generate new values\n- Lookups can enrich data by fetching related information\n- Mode determines how records are processed (create new or modify existing)\n","properties":{"version":{"type":"string","description":"Version of the expression format. Determines which rules\nproperty contains the transformation logic.\n","enum":["1","2"]},"rules":{"type":"array","description":"Transformation rules for version 1 expressions. An array of\nrule groups; each group is an array of field-mapping objects.\nMost transforms have a single group. Present when `version`\nis `\"1\"`. The output record contains ONLY the generated\nfields — every unmapped field is dropped (v1 has no\nequivalent of Transform 2.0's `modify` mode), and the\nrecord's trace key does not survive the rebuild.\n","items":{"type":"array","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path to read from. Supports multiple\nsyntaxes: bare field names (`id`), dot notation\n(`fulfillment.shipment_id`), slash-prefixed paths\nfor XML (`/FeedProcessingStatus`), wildcards (`*.id`,\n`*.[Internal ID]`), and array indexing (`SDF[0]`).\n"},"generate":{"type":"string","description":"Target field name to write to. Typically a bare name\n(`id`) or dot path (`SDF.Filter.ID`).\n"},"key":{"type":"string","description":"Auto-generated identifier for this rule, used by the\nUI to track individual rules for editing and reordering.\n"}},"required":["extract","generate"]}}},"rulesTwoDotZero":{"type":"object","description":"Configuration for version 2 transformation rules. This object contains the core logic\nfor how data is mapped, enriched, and transformed.\n\n**Capabilities**\n\nTransformation 2.0 provides:\n- Precise field mapping with JSONPath expressions\n- Support for deeply nested data structures\n- Formula-based field generation\n- Dynamic lookups for data enrichment\n- Multiple operating modes to fit different scenarios\n","properties":{"mode":{"type":"string","description":"Transformation mode that determines how records are handled during processing.\n\n**Available modes**\n\n**Create Mode (`\"create\"`)**\n- **Behavior**: Builds entirely new output records from inputs\n- **Use When**: Output structure differs significantly from input\n- **Advantage**: Clean slate approach, no field inheritance\n\n**Modify Mode (`\"modify\"`)**\n- **Behavior**: Makes targeted edits to existing records\n- **Use When**: Output structure should remain similar to input\n- **Advantage**: Preserves unmapped fields from the original record\n","enum":["create","modify"]},"mappings":{"$ref":"#/components/schemas/Mappings"},"lookups":{"allOf":[{"description":"Shared lookup tables used across all mappings defined in the transformation rules.\n\n**Purpose**\n\nLookups provide centralized value translation that can be referenced from any mapping\nin your transformation configuration. They enable consistent translation of codes, IDs,\nand values between systems without duplicating translation logic.\n\n**Usage in transformations**\n\nLookups are particularly valuable in transformations for:\n\n- **Data Normalization**: Standardizing values from diverse source systems\n- **Code Translation**: Converting between different coding systems (e.g., status codes)\n- **Field Enrichment**: Adding descriptive values based on ID or code lookups\n- **Cross-Reference Resolution**: Mapping identifiers between integrated systems\n\n**Implementation**\n\nLookups are defined once in this array and referenced by name in mappings:\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"statusMapping\",\n    \"map\": {\n      \"A\": \"Active\",\n      \"I\": \"Inactive\",\n      \"P\": \"Pending\"\n    },\n    \"default\": \"Unknown Status\"\n  }\n]\n```\n\nThen referenced in mappings using the lookupName property:\n\n```json\n{\n  \"generate\": \"status\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.statusCode\",\n  \"lookupName\": \"statusMapping\"\n}\n```\n\nThe system automatically applies the lookup during transformation processing.\n\nFor complete details on lookup properties and behavior, see the Lookups schema.\n"},{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the JSON shape the transformTwoDotZero processor\nevaluates `mappings[].extract` JSONPath values against at\nflow runtime. Applies only to Transform 2.0 (v2,\n`rulesTwoDotZero`); v1 transforms (the `rules` array on\n`transform.expression.rules`) and script-mode transforms\nignore this field.\n"}}}}},"script":{"type":"object","description":"Configuration for programmable script-based transformations. This object enables complex, custom\ntransformation logic beyond what expression-based transformations can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to transform data according to\nspecialized business rules or complex algorithms.\n\n**Implementation approach**\n\nScript-based transformation works by:\n1. Executing the specified function from the referenced script\n2. Passing input data to the function\n3. Using the function's return value as the transformed output\n\n**Common use cases**\n\nScript transformation is ideal for:\n- Complex business logic that can't be expressed through mappings\n- Algorithmic transformations requiring computation\n- Dynamic transformations based on external factors\n- Legacy system data format compatibility\n- Multi-stage processing with intermediate steps\n\nOnly use script-based transformation when expression-based transformation is insufficient.\nScript transformation requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to a predefined script resource containing the transformation logic.\n\nThe referenced script should contain the function specified in the\n'function' property. It must be a script in this account: any other id fails the save\nwith 422 (\"Only a script owned by the user or a script shared by the Integration App can\nbe used at transform.script._scriptId.\").\n","format":"objectid"},"function":{"type":"string","description":"Name of the function within the script to execute for transformation. This function\nmust exist in the script referenced by _scriptId.\n"}}}}},"Lookups":{"type":"array","description":"Configuration for value-to-value transformations using lookup tables.\n\n**Purpose**\n\nLookups provide a way to translate values from one system to another. They transform\ninput values into output values using either static mapping tables or\ndynamic lookup caches.\n\n**Lookup mechanisms**\n\nThere are two distinct lookup mechanisms available:\n\n1. **Static Lookups**: Define a simple key-value map object and store it as part of your resource\n   - Best for: Small, fixed sets of values that rarely change\n   - Implementation: Configure the `map` object with input-to-output value mappings\n   - Example: Country codes, status values, simple translations\n\n2. **Dynamic Lookups**: Reference an existing 'Lookup Cache' resource in your Celigo account\n   - Best for: Large datasets, frequently changing values, or complex reference data\n   - Implementation: Configure `_lookupCacheId` to reference cached data maintained independently\n   - Example: Product catalogs, customer databases, pricing information\n\n**Property usage**\n\nThere are two mutually exclusive ways to configure lookups, depending on which mechanism you choose:\n\n1. **For Static Mappings**: Configure the `map` property with a direct key-value object\n   ```json\n   \"map\": {\"US\": \"United States\", \"CA\": \"Canada\"}\n   ```\n\n2. **For Dynamic Lookups**: Configure the following properties:\n   - `_lookupCacheId`: Reference to the lookup cache resource\n   - `extract`: JSON path to extract specific value from the returned lookup object\n\n**When to use**\n\nLookups are ideal for:\n\n1. **Value Translation**: Mapping codes or IDs to human-readable values\n\n2. **Data Enrichment**: Adding related information to records during processing\n\n3. **Normalization**: Ensuring consistent formatting of values across systems\n\n**Implementation details**\n\nLookups can be referenced in:\n\n1. **Field Mappings**: Direct use in field transformation configurations\n\n2. **Handlebars Templates**: Use within templates with the syntax:\n   ```\n   {{lookup 'lookupName' record.fieldName}}\n   ```\n\n**Example usage**\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"countryCodeToName\",\n    \"map\": {\n      \"US\": \"United States\",\n      \"CA\": \"Canada\",\n      \"UK\": \"United Kingdom\"\n    },\n    \"default\": \"Unknown Country\",\n    \"allowFailures\": true\n  },\n  {\n    \"name\": \"productDetails\",\n    \"_lookupCacheId\": \"60a2c4e6f321d800129a1a3c\",\n    \"extract\": \"$.details.price\",\n    \"allowFailures\": false\n  }\n]\n```\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for the lookup table within this configuration.\n\nThis name must be unique within the scope where the lookup is defined and is used to reference\nthe lookup in handlebars templates with the syntax {{lookup 'name' value}}.\n\nChoose descriptive names that indicate the transformation purpose, such as:\n- \"countryCodeToName\" for country code to full name conversion\n- \"statusMapping\" for status code translations\n- \"departmentCodes\" for department code to name mapping\n"},"map":{"type":["object","null"],"description":"The lookup mapping table as key-value pairs. The platform stores `null`\nhere on dynamic lookups, which resolve values at runtime instead of\nfrom a static table.\n\nThis object contains the input values as keys and their corresponding\noutput values. When a input value matches a key in this object,\nit will be replaced with the corresponding value.\n\nThe map should be kept to a reasonable size (typically under 100 entries)\nfor optimal performance. For larger mapping requirements, consider using\ndynamic lookups instead.\n\nMaps can include:\n- Simple code to name conversions: {\"US\": \"United States\"}\n- Status transformations: {\"A\": \"Active\", \"I\": \"Inactive\"}\n- ID to name mappings: {\"100\": \"Marketing\", \"200\": \"Sales\"}\n\nValues can be strings, numbers, or booleans, but all are stored as strings\nin the configuration.\n"},"_lookupCacheId":{"type":"string","description":"Reference to a LookupCache resource that contains the reference data for the lookup.\n\n**Purpose**\n\nThis field connects the lookup to an external data source that has been cached in the system.\nUnlike static lookups that use the `map` property, dynamic lookups can reference large datasets\nor frequently changing information without requiring constant updates to the integration.\n\n**Implementation details**\n\nThe LookupCache resource referenced by this ID contains:\n- The data records to be used as a reference source\n- Configuration for how the data should be indexed and accessed\n- Caching parameters to balance performance with data freshness\n\n**Usage patterns**\n\nCommonly used to reference:\n- Product catalogs or SKU databases\n- Customer or account information\n- Pricing tables or discount rules\n- Complex business logic lookup tables\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n","format":"objectid"},"extract":{"type":"string","description":"JSON path expression that extracts a specific value from the cached lookup object.\n\n**Purpose**\n\nWhen using dynamic lookups with a LookupCache, this JSON path identifies which field to extract\nfrom the cached object after it has been retrieved using the lookup key.\n\n**Implementation details**\n\n- Must use JSON path syntax (similar to mapping extract fields)\n- Operates on the cached object returned by the lookup operation\n- Examples:\n  - \"$.name\" - Extract the name field from the top level\n  - \"$.details.price\" - Extract a nested price field\n  - \"$.attributes[0].value\" - Extract a value from the first element of an array\n\n**Usage scenario**\n\nWhen a lookup cache contains complex objects:\n```json\n// Cache entry for key \"PROD-123\":\n{\n  \"id\": \"PROD-123\",\n  \"name\": \"Premium Widget\",\n  \"details\": {\n    \"price\": 99.99,\n    \"currency\": \"USD\",\n    \"inStock\": true\n  }\n}\n```\n\nSetting extract to \"$.details.price\" would return 99.99 as the lookup result.\n\nIf no extract is provided, the entire cached object is returned as the lookup result.\n"},"default":{"type":["string","null"],"description":"Default value to use when the source value is not found in the lookup map.\nThe platform stores `null` here when no default is configured.\n\nThis value is used as a fallback when:\n1. The source value doesn't match any key in the map\n2. allowFailures is true or omitted\n\nSetting an appropriate default helps prevent flow failures due to unexpected\nvalues and provides predictable behavior for edge cases.\n\nCommon default patterns include:\n- Descriptive unknowns: \"Unknown Country\", \"Unspecified Status\"\n- Original value indicators: \"{Original Value}\", \"No mapping found\"\n- Neutral values: \"Other\", \"N/A\", \"Miscellaneous\"\n\nA save refuses a default together with an explicit `allowFailures: false`\n(422 `Lookups with default set can not have allowFailures explicitly set to\nfalse.`): a lookup either fails a miss (`allowFailures: false`, no default) or\nfalls back to its default.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, missing lookup values will use the default value rather than causing an error.\n\n**Behavior control**\n\nThis field determines how the system handles source values that don't exist in the map:\n\n- true: Use the default value for missing mappings and continue processing\n- false: Treat missing mappings as errors, failing the record (`lookup_error` for a\n  static map, `lookupcache_evaluation_failed` for a lookup cache)\n- omitted or null: the record doesn't fail either. On an import a static lookup writes\n  its default if it has one, and a static or cache lookup without one leaves the\n  mapped field out of the output, with no error\n\n**Recommendation**\n\nSet this to true when:\n- New source values might appear over time\n- Data quality issues could introduce unexpected values\n- Processing should continue even with imperfect mapping\n\nSet this to false when:\n- Complete data accuracy is critical\n- All possible source values are known and controlled\n- Missing mappings indicate serious data problems that should be addressed\n\nA default raises no error: records that miss go out with the default value and\nnothing reports them. Use true only when that fallback is acceptable at the\ndestination, and set false explicitly when a missing value must stop the record.\n"}}}},"Form":{"type":"object","description":"Configuration for creating user-friendly settings forms that make it easier for less technical users\nto configure integration resources.\n\n**Settings form builder**\n\nThe Settings Form Builder allows you to create or edit user-friendly fields that prompt for text entry\nor selections that will be returned as settings applied to this resource. Your forms can include any\nfield types that you see elsewhere in integrator.io, such as:\n\n- Text fields\n- Dropdown selections\n- Checkboxes\n- Radio buttons\n- Date pickers\n- Multi-select fields\n- Search fields\n\nForm fields make it much easier for less technical users to work with your integration settings by:\n\n- Providing clear labels and help text\n- Enforcing validation rules\n- Offering pre-defined selection options\n- Grouping related settings logically\n- Supporting conditional visibility\n- Creating a consistent user experience\n","properties":{"form":{"type":"object","description":"Configuration that defines the structure, fields, and behavior of the settings form.\n\nThis object contains the complete definition of the form's layout, fields, validation rules,\nand interactive behaviors. The specific structure depends on the form complexity and can include\nfield definitions, sections, conditional display logic, and default values.\n\nThe form configuration is typically created and managed through the visual Form Builder interface\nrather than edited directly as JSON.\n","properties":{"fieldMap":{"type":"object","description":"A mapping of field identifiers to their configuration objects.\nEach key in this object represents a unique field ID, and the value contains\nall the configuration settings for that specific form field.\n","additionalProperties":{"type":"object","description":"Configuration for an individual form field.\n","properties":{"id":{"type":"string","description":"Unique identifier for this field within the form: layout containers and conditions\nrefer to the field by it. Use the same string as the field's fieldMap key and its `name`.\n"},"name":{"type":"string","description":"Key the field's value is saved under: one top-level key of the resource's `settings`,\nwhich the form also reads the value back from when it opens. Nested objects inside\n`settings` are written to it directly, not through a form field's `name`.\n"},"type":{"type":"string","description":"The type of form control to render for this field.\n","enum":["text","checkbox","radiogroup","relativeuri","editor","keyvalue","select","multiselect","toggle","datetime","date","exportSelect","staticMap"]},"label":{"type":"string","description":"Display label shown next to the field in the form.\n"},"description":{"type":"string","description":"Detailed explanation text that appears below the field, providing more context\nthan the label or helpText.\n"},"helpText":{"type":"string","description":"Explanatory text that appears when hovering over the help icon next to the field.\nUsed to provide additional guidance on how to use the field.\n"},"required":{"type":"boolean","description":"When true, the field must have a value before the form can be submitted. Only the form\nenforces it: an API write of `settings` is stored without the value.\n","default":false},"defaultValue":{"description":"Value the form shows while the resource's `settings` holds no value under the field's\n`name`.\n"},"multiline":{"type":"boolean","description":"For text fields, determines whether the input should be a multi-line text area\ninstead of a single-line input.\n","default":false},"rowsMax":{"type":"integer","description":"For multiline text fields, specifies the maximum number of visible rows.\n"},"inputType":{"type":"string","description":"For text fields, specifies the HTML input type attribute to apply additional\nvalidation or specialized input behavior.\n","enum":["text","number","email","password","tel","url"]},"delimiter":{"type":"string","description":"For text fields, specifies a character to use for splitting the input into an array.\nUsed for collecting multiple values in a single text field.\n"},"mode":{"type":"string","description":"For editor fields, specifies the type of content being edited for syntax highlighting.\n","enum":["json","xml","csv","text"]},"keyName":{"type":"string","description":"For keyvalue and staticMap fields, names the first column: each saved row holds that\ncolumn's value under this key. A keyvalue field without it uses `key`.\n"},"valueName":{"type":"string","description":"For keyvalue and staticMap fields, names the second column: each saved row holds that\ncolumn's value under this key. A keyvalue field without it uses `value`.\n"},"keyLabel":{"type":"string","description":"For staticMap fields, heading of the first column.\n"},"valueLabel":{"type":"string","description":"For staticMap fields, heading of the second column.\n"},"keyOptions":{"type":"array","description":"For staticMap fields, suggestions offered in the first column's cells. A cell still\naccepts a typed value.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"Display text for the suggestion.\n"},"value":{"type":"string","description":"Value saved when the suggestion is chosen.\n"}}}},"valueOptions":{"type":"array","description":"For staticMap fields, suggestions offered in the second column's cells. A cell still\naccepts a typed value.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"Display text for the suggestion.\n"},"value":{"type":"string","description":"Value saved when the suggestion is chosen.\n"}}}},"keyResource":{"type":"object","description":"For staticMap fields, fills the first column's suggestions from an export the form\ncarries inline in `virtual`, run the way an exportSelect's `resource` is. With\n`keyResource` or `valueResource` set, the form requires both columns in every row\nunless `keyValueRequired` is false.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline, shaped like an exportSelect's `resource.virtual`.\n","additionalProperties":true}}},"valueResource":{"type":"object","description":"For staticMap fields, fills the second column's suggestions from an export the form\ncarries inline in `virtual`, run the way an exportSelect's `resource` is.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline, shaped like an exportSelect's `resource.virtual`.\n","additionalProperties":true}}},"keyValueRequired":{"type":"boolean","description":"For staticMap fields with `keyResource` or `valueResource`, when false, saves rows\nthat fill only one column; by default the form drops them.\n","default":true},"resource":{"type":"object","description":"For exportSelect fields, the export whose records become the options. When the form\nopens, the UI previews the export in `virtual` (with `test.limit` 100 unless the export\nsets its own) and lists the preview's `data`: each record needs a `label` and a `value`\n(a string or number serves as both), so shape them with the export's `transform`. A\nsaved export can't be referenced, and an HTTP export that finds no records leaves the\nfield showing an error instead of options.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline: `adaptorType`, its adaptor block, and an optional\n`transform`. `_connectionId` defaults to the connection of the export or import the\nform belongs to; set it on any other resource's form.\n","additionalProperties":true}}},"multiselect":{"type":"boolean","description":"For exportSelect fields, when true, the user can choose several options and the value\nis saved as an array.\n","default":false},"supportHandlebars":{"type":"boolean","description":"For exportSelect fields, when true, renders the `resource.virtual` export as a Handlebars\ntemplate before each preview (all but its `transform`, `hooks`, and `filter`). On an\nintegration, flow group, flow, export, or import form, the form's current values are\nunder that form's scope name (`integration`, `flowGrouping`, `flow`, `export`, or\n`import`), beside the saved settings of the scopes above it, with no `settings.`\nprefix; on a connection or iClient form the template has no context. The options then\nload through `refreshOptionsOnChangesTo` or `typeAheadSearch`, or when the user\nrefreshes the field.\n","default":false},"refreshOptionsOnChangesTo":{"type":"array","items":{"type":"string"},"description":"For exportSelect fields with `supportHandlebars`, ids of the fields whose values the\ntemplated export uses: the options load once one of them has a value and reload whenever\none changes.\n"},"typeAheadSearch":{"type":"boolean","description":"For exportSelect fields, when true, re-runs the templated export as the user types, with\nthe typed text as this field's own value; an empty box clears the options, and typed\ntext is accepted as the value.\n","default":false},"showDelete":{"type":"boolean","description":"For keyvalue fields, determines whether to show a delete button for each key-value pair.\n"},"doNotAllowFutureDates":{"type":"boolean","description":"For date and datetime fields, restricts selection to dates not in the future.\n"},"skipTimezoneConversion":{"type":"boolean","description":"For datetime fields, prevents automatic timezone conversion of the date value.\n"},"options":{"type":"array","description":"For fields that present choices (select, multiselect, radiogroup, toggle), defines\nthe available options.\n","items":{"anyOf":[{"title":"Option group","type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"oneOf":[{"title":"String value","type":"string"},{"title":"Label-value pair","type":"object","properties":{"label":{"type":"string","description":"Display text for the option.\n"},"value":{"type":"string","description":"Value to store when this option is selected.\n"}}}]},"description":"Array of option values/labels to display in the selection control.\n"}}},{"title":"Label-value pair","type":"object","required":["label"],"properties":{"label":{"type":"string","description":"Display text for the option.\n"},"value":{"type":"string","description":"Value to store when this option is selected.\n"}}}]}},"visibleWhen":{"type":"array","description":"Conditional display rules that determine when this field should be visible.\nIf empty or not provided, the field is always visible.\n","items":{"type":"object","properties":{"field":{"type":"string","description":"The ID of another field whose value controls the visibility of this field. The\nsave doesn't check it: a rule naming an ID that isn't in the form counts as met,\nso the field stays visible.\n"},"is":{"type":"array","items":{"type":["string","boolean","number","null"]},"description":"Array of values - if the referenced field has any of these values,\nthis field will be visible. Values may be strings, booleans (for\ncheckbox/toggle fields), numbers (for numeric inputs), or null.\n"}}}}}}},"layout":{"type":"object","description":"Defines how the form fields are arranged and grouped in the UI.\nThe layout can organize fields into columns, sections, or other visual groupings.\n","properties":{"type":{"type":"string","description":"The type of layout to use for the form.\n","enum":["column","collapse","box","indent","tabWithoutSave","verticalTabWithoutSave"]},"fields":{"type":"array","items":{"type":"string"},"description":"Field IDs shown above the containers, without a heading.\n"},"containers":{"type":"array","description":"Array of container objects that group fields or contain nested containers.\nEach container can represent a column, box, indented section, or collapsible section.\n","items":{"type":"object","properties":{"type":{"type":"string","description":"The visual style of the container.\n","enum":["indent","box","collapse"]},"label":{"type":"string","description":"The heading text displayed for this container.\n"},"fields":{"type":"array","items":{"type":"string"},"description":"Array of field IDs that should be displayed in this container.\nEach ID must correspond to a key in the fieldMap object: the save doesn't check it,\nand an ID that doesn't renders nothing. A field the layout doesn't list isn't shown.\n"},"containers":{"type":"array","description":"Nested containers within this container. Allows for hierarchical organization\nof fields with different visual styles.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"The heading text displayed for this nested container.\n"},"fields":{"type":"array","items":{"type":"string"},"description":"Array of field IDs that should be displayed in this nested container.\n"}}}}}}}}}},"additionalProperties":true},"init":{"type":"object","description":"Configuration for custom JavaScript initialization that executes when the form is first loaded.\n\nThis object defines a JavaScript hook that prepares the form for use, sets initial field values,\nperforms validation, or otherwise customizes the form behavior before it is displayed to the user.\n\n**Function signature**\n\nThe initialization function is invoked with a single 'options' argument containing contextual information:\n```javascript\nfunction formInit(options) {\n  // Process options and return the form object\n  return options.resource.settingsForm.form;\n}\n```\n\n**Available context**\n\nThe 'options' argument provides access to:\n- `options.resource` - The current resource being configured\n- `options.parentResource` - The parent of the current resource\n- `options.grandparentResource` - The grandparent of the current resource\n- `options.license` - For integration apps, the license provisioned to the integration\n- `options.parentLicense` - For integration apps, the parent of the license\n- `options.sandbox` - Boolean flag indicating whether the script is running in a sandbox environment\n\n\n**Common uses**\n\n- Dynamically generate field options based on resource configuration\n- Pre-populate default values from related resources\n- Apply conditional logic that depends on resource properties\n- Add, remove, or modify form fields based on user permissions or account settings\n- Fetch external data to populate selection options\n- Implement complex validation rules that depend on resource context\n- Create branching form experiences based on user selections\n\n**Return value**\n\nThe function must return a valid form object that the UI can render.\nThrowing an exception will signal an error to the user.\n\nThe UI runs the function each time it opens the form and renders the returned form instead of\n`form`, then fills in the values from `settings`; a thrown error's message replaces the form.\n","properties":{"function":{"type":"string","description":"The name of the function to execute within the referenced script.\n\nThis property specifies which function to invoke from the script\nreferenced by _scriptId. The function will be called when the form\nis initialized and should handle any custom setup logic.\n\nThe function must follow the expected signature and return a valid form object.\n"},"_scriptId":{"type":"string","format":"objectId","description":"Reference to a predefined script resource containing the initialization function.\n\nThe referenced script should contain the function specified in the\n'function' property. On an integration, a flow group, a flow, an export, an import, or a\nconnection, an id that isn't a script in the same account (or, on an Integration App's\nresources, one the app shares) is refused with 422 `invalid_ref`. An iClient save\nstores the id without that check.\n"}}}}},"Settings":{"type":"object","description":"Configuration settings that can be accessed by hooks, filters, mappings and handlebars templates at runtime.\n\nIt enables customization of the resource's logic, allowing hooks, mappings, filters, and\nhandlebars to access and apply the settings at runtime.\n\n**Usage**\n\nThe settings object can store arbitrary JSON data that you want to save with the resource.\nWhile it's often populated through a form defined in the `settingsForm` field, you can also:\n\n- Directly provide JSON settings without using a form\n- Store configuration values used by hooks and templates\n- Create resource-specific constants and parameters\n- Maintain lookup tables or mapping structures\n- Define conditional logic parameters\n\n**Accessibility**\n\nSettings are available in:\n- All handlebars fields for building dynamic payloads\n- Field mapping expressions\n- JavaScript hooks via the options object\n- Filters and transformations\n\nAt runtime, the settings objects in the step's execution chain are gathered into a\nsingle `settings` context keyed by fixed scope keys. Flow runs populate the full\nchain: `settings.integration.*`, `settings.flowGrouping.*` (the flow group the flow\nbelongs to), `settings.flow.*`, `settings.connection.*`, `settings.iClient.*`, and\nthe running step's own scope — `settings.export.*` on exports/lookups,\n`settings.import.*` on imports (the key matches the step type). Steps executing\ninside a My API or a Tool receive only the step's own scope plus\n`settings.connection.*` / `settings.iClient.*`; the integration, flowGrouping, and\nflow scopes resolve empty there. The scope key is literal — resource display names\nand settingsForm section labels are never part of the path. The segments after the\nscope mirror the stored settings JSON key path exactly: a top-level field is\n`settings.<scope>.<fieldId>`, and nested objects add one segment per JSON key\n(e.g. `settings.flowGrouping.Customer.region` when the group's settings JSON nests\n`region` under `Customer`). References without a scope key\n(e.g. `{{settings.myField}}`) resolve to empty strings.\n\n**Best practices**\n\nFor non-technical users, create a custom form instead of editing the JSON directly.\nThis provides a user-friendly interface for updating settings without requiring JSON knowledge.\n","additionalProperties":true},"MockOutput":{"type":["object","null"],"description":"Sample data that simulates the output from an export for testing and configuration purposes.\n\nMock output allows you to configure and test flows without executing the actual export or\nwaiting for real-time data to arrive. This is particularly useful for:\n- Initial flow configuration and testing\n- Mapping development without requiring live data\n- Generating metadata for downstream flow steps\n- Creating realistic test scenarios\n- Documenting expected data structures\n\n**Structure**\n\nThe mock output must follow the integrator.io canonical format, which consists of a\n`page_of_records` array containing record objects. Each record object has a `record`\nproperty that contains the actual data fields.\n\n```json\n{\n  \"page_of_records\": [\n    {\n      \"record\": {\n        \"field1\": \"value1\",\n        \"field2\": \"value2\",\n        ...\n      }\n    },\n    ...\n  ]\n}\n```\n\n**Usage**\n\nIn a flow test run, and for an upstream lookup during a page-processor preview\n(`previewPageProcessor`), integrator.io substitutes this mock output for the export's live\nresponse instead of calling the source. A saved mock is not read anywhere else: a target\nlookup in that preview runs its live query, invoking the export\n(`POST /v1/exports/{_id}/invoke`) reads the source, and real runs ignore it. This allows you\nto:\n- Test mappings with representative data\n- Configure downstream flow steps without waiting for real data\n- Simulate various data scenarios\n\nWhere it is substituted, the records stand in for the RAW parsed response: the export's own\n`transform` and output `filter` still run on top of them, so downstream steps — and a\nlookup's results mapping — see the transformed, filtered records, not the shape stored here.\n\nIn a flow test run of an HTTP export with a saved mock (live probe, 2026-10-21), the export\nstill listed `request` and `response` stages: `request` held the request the export composes,\nand `response` held the mock under the source's URL, as did the test run's request log, which\nrecorded it as a 200. Read those stages as the substituted mock, not as a live call.\n\n`POST /v1/exports/preview` reads the `mockOutput` in its request body, not the saved one. For\nan export the preview supports (an HTTP export, for example), a body that carries it (as an\nexport read back with GET does) returns that snapshot as a lone `parse` stage without calling\nthe source; remove `mockOutput` from the body to preview live records.\n\n**Limitations**\n\n- Maximum of 10 records\n- Maximum size of 1 MB\n- Must follow the canonical format shown above\n\nMock output can be populated automatically from preview data (which is the parsed response,\nbefore `transform` / `filter`) or entered manually.\n","properties":{"page_of_records":{"type":"array","description":"Array of record objects in the integrator.io canonical format.\n\nEach item in this array represents one record that would be processed\nby the flow during execution.\n","items":{"type":"object","properties":{"record":{"type":"object","description":"Container for the actual record data fields.\n\nThe structure of this object will vary depending on the specific\nexport configuration and the source system's data structure.\n","additionalProperties":true}}}}}},"PreSave":{"type":"object","description":"Defines a JavaScript hook that executes before the resource is saved.\n\nThis hook allows for programmatic validation, transformation, or enrichment of the\nresource itself before it is persisted. It can be used to enforce business rules,\nset derived properties, or implement cross-field validations that can't be expressed\nthrough the standard UI.\n\n**Function signature**\n\nThe preSave function is invoked with a single 'options' argument containing:\n```javascript\nfunction preSave(options) {\n  // Process options and return the modified resource\n  return options.newResource;\n}\n```\n\n**Available context**\n\nThe 'options' argument provides access to:\n- `options.newResource` - The resource being saved (with pending changes)\n- `options.oldResource` - The previous version of the resource (before changes)\n- `options.sandbox` - Boolean flag indicating whether the script is running in a sandbox environment\n\n\n**Common uses**\n\n- Enforcing complex business rules across multiple fields\n- Automatically deriving field values based on other configuration\n- Performing validation that depends on external systems or data\n- Normalizing or standardizing configuration values\n- Adding computed or derived properties\n- Implementing versioning or change tracking\n- Dynamically looking up data using the Celigo API module to enrich configuration\n\n**Return value**\n\nThe function must return the newResource object (potentially modified) to be saved.\nThrowing an exception will prevent saving and signal an error to the user.\n","properties":{"function":{"type":"string","description":"The name of the function to execute within the referenced script.\n\nThis property specifies which function to invoke from the script\nreferenced by _scriptId. The function will be called just before\nthe resource is saved.\n\nThe function must follow the expected signature and return the resource object.\n"},"_scriptId":{"type":"string","format":"objectId","description":"Reference to a predefined script resource containing the preSave function.\n\nThe referenced script should contain the function specified in the\n'function' property. This script must be accessible within the user's account\nand have appropriate permissions.\n"}}},"ResourceResponse":{"type":"object","description":"Response","properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Unique identifier for the resource. Format is a 24-character hexadecimal string."},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was created. Set automatically and cannot be modified."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was last updated. Changes whenever any property is modified."},"deletedAt":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp when the resource was soft-deleted. When null or absent, the resource is active."}},"required":["_id"]},"IAResourceResponse":{"type":"object","description":"Integration app response fields for resources that are part of integration apps","properties":{"_integrationId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the specific integration instance that contains this resource.\n\nThis field is only populated for resources that are part of an integration app\ninstallation. It contains the unique identifier (_id) of the integration\nresource that was installed in the account.\n\nThe integration instance represents a specific installed instance of an\nintegration app, with its own configuration, settings, and runtime environment.\n\nThis reference enables:\n- Tracing the resource back to its parent integration instance\n- Permission and access control based on integration ownership\n- Lifecycle management (enabling/disabling, updating, or uninstalling)\n"},"_connectorId":{"type":"string","format":"objectId","readOnly":true,"description":"Integration App (connector) that defines this resource. Present only on resources an\nIntegration App owns. The app locks parts of these documents, and a PUT that changes a\nlocked field answers 200 but keeps the stored value, so read the document back to confirm\na change."}}},"AIDescription":{"type":"object","description":"AI-generated descriptions and documentation for the resource.\n\nThis object contains automatically generated content that helps users\nunderstand the purpose, behavior, and configuration of the resource without\nrequiring them to analyze the technical details. The AI-generated content\nis sanitized and safe for display in the UI.\n","properties":{"summary":{"type":["string","null"],"description":"Brief AI-generated summary of the resource's purpose and functionality.\n\nThis concise description provides a quick overview of what the resource does,\nwhat systems it interacts with, and its primary role in the integration.\nThe summary is suitable for display in list views, dashboards, and other\ncontexts where space is limited.\n\nMaximum length: 10KB\n"},"detailed":{"type":["string","null"],"description":"Comprehensive AI-generated description of the resource's functionality.\n\nThis detailed explanation covers the resource's purpose, configuration details,\ndata flow patterns, filtering logic, and other technical aspects. It provides\nin-depth information suitable for documentation, tooltips, or detailed views\nin the administration interface.\n\nThe content may include HTML formatting for improved readability.\n\nMaximum length: 10KB\n"},"generatedOn":{"type":["string","null"],"format":"date-time","description":"Timestamp indicating when the AI description was generated.\n\nThis field helps track the freshness of the AI-generated content and\ndetermine when it might need to be regenerated due to changes in the\nresource's configuration or behavior.\n\nThe timestamp is recorded in ISO 8601 format with UTC timezone (Z suffix).\n"}}},"APIM":{"type":"array","description":"Read-only field that stores information about the integration resources\npublished in the API Management (APIM) platform.\n\nThis field tracks the relationship between integrator.io resources and their\npublished counterparts in the APIM platform, which is\ntightly integrated with the Celigo UI. When resources are \"pushed\" to APIM,\nthis field is populated with the relevant identifiers and statuses.\n","items":{"type":"object","properties":{"apiId":{"type":"string","description":"Identifier for the API where this integrator.io resource is published in the APIM.\n\nThis is an APIM resource identifier (not prefixed with underscore like Celigo IDs)\nthat uniquely identifies the API in the API Management platform.\n"},"flowId":{"type":"string","description":"Identifier for the flow within the API where this integrator.io resource is linked.\n\nWhen an API has multiple integrator.io resources linked, each resource is associated\nwith a specific flow in the API, identified by this field. This is an APIM\nresource identifier.\n"},"status":{"type":"string","description":"Indicates the publishing stage of the integrator.io resource in APIM.\n\nPossible values:\n- 'oaspending': The resource is published but the OpenAPI Specification (OAS) is not\n  yet published. The apiId will be updated with the API ID created in APIM.\n- 'published': The OpenAPI Specification for the integrator.io resource has been\n  successfully uploaded to APIM.\n","enum":["oaspending","published"]}}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"400-bad-request":{"description":"Bad request. The server could not understand the request because of malformed syntax or invalid parameters.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/exports/{_id}/clone":{"post":{"summary":"Clone an export","description":"Creates a copy of an existing export, remapping its connection through `connectionMap`.\nFor an export that uses a connection, omitting the map or sending an empty one fails with\n400 `invalid_connectionMap`; map the connection to itself to keep it.\n","operationId":"cloneExport","tags":["Exports"],"parameters":[{"name":"_id","in":"path","description":"The unique identifier of the export to clone","required":true,"schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloneRequest"}}}},"responses":{"201":{"description":"Export cloned successfully. Returns a manifest of the resource the clone created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloneResponse"}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
````

## Replace connection on export present in a flow

> Replaces the connection used by an export in a flow and cancels any running jobs.\
> This is useful when migrating flows between environments or updating to newer connection versions.<br>

```json
{"openapi":"3.2.0","info":{"title":"Exports","version":"1.0.0"},"tags":[{"name":"Exports","description":"Exports retrieve data from source systems — on a schedule, in delta mode, in real time,\non demand, or as a file/blob transfer — package the results into ≤ 5-MB pages, and pass\neach page to downstream flow steps. Depending on configuration, an export surfaces in the\nFlow Builder as an export, a real-time listener, a file transfer, or a mid-flow lookup.\n\n## Export schema\n\n{% openapi-schemas spec=\"export\" schemas=\"Export\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"responses":{"400-bad-request":{"description":"Bad request. The server could not understand the request because of malformed syntax or invalid parameters.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}}},"paths":{"/v1/exports/{_id}/replaceConnection":{"put":{"summary":"Replace connection on export present in a flow","description":"Replaces the connection used by an export in a flow and cancels any running jobs.\nThis is useful when migrating flows between environments or updating to newer connection versions.\n","operationId":"replaceConnectionOnExport","tags":["Exports"],"parameters":[{"name":"_id","in":"path","description":"The unique identifier of the export","required":true,"schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"_newConnectionId":{"type":"string","description":"The id of the new connection to be used"}},"required":["_newConnectionId"]}}}},"responses":{"204":{"description":"Successfully replaced connection on export"},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
```

## Preview the output of an export doc (no job created)

> Runs an export doc through the flow engine's preview pipeline and returns\
> the sample data it would have emitted, along with stage-by-stage\
> diagnostics and any errors encountered. \*\*No Job record is created\*\* and\
> no flow-level state is updated — this is a stateless preview.\
> \
> Body is a complete export document (the shape you would POST to\
> \`/v1/exports\`), typically without \`\_id\`. The CLI uses it for two\
> scenarios:\
> \- \`celigo exports invoke\` with a doc on stdin → ad-hoc preview of a\
> &#x20; not-yet-saved export.\
> \- Agent-driven "preview + refine" loops where an LLM iterates on the\
> &#x20; export config and calls this endpoint to sample output each time.\
> \
> The scoped variant at\
> \`POST /v1/integrations/{\_integrationId}/flows/{\_flowId}/exports/preview\`\
> does the same thing but inherits flow + integration context (useful when\
> the export references flow-scoped settings). Prefer the unscoped variant\
> when previewing a standalone export.\
> \
> Use \`test.limit\` inside the body to cap the number of records\
> returned. Configuration errors in the export doc surface in the\
> \`errors\[]\` and \`stages\[].errors\[]\` arrays within a 200 response, but\
> not every failure does: structural validation failures (e.g. missing\
> \`\_connectionId\`) return 4xx, and so does an output filter that can't\
> read a record's value (see the 422 response).

```json
{"openapi":"3.2.0","info":{"title":"Exports","version":"1.0.0"},"tags":[{"name":"Exports","description":"Exports retrieve data from source systems — on a schedule, in delta mode, in real time,\non demand, or as a file/blob transfer — package the results into ≤ 5-MB pages, and pass\neach page to downstream flow steps. Depending on configuration, an export surfaces in the\nFlow Builder as an export, a real-time listener, a file transfer, or a mid-flow lookup.\n\n## Export schema\n\n{% openapi-schemas spec=\"export\" schemas=\"Export\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"ExportPreviewResponse":{"type":"object","description":"Envelope returned by `POST /v1/exports/preview` (and the scoped\n`/v1/integrations/{_integrationId}/flows/{_flowId}/exports/preview`\nvariant). Carries per-stage diagnostics alongside the sampled records.","properties":{"data":{"type":"array","description":"Final sampled records produced by the preview — the output of the last\nstage, without the wrappers the transform, filter and hook stages put\naround it, so records the export's output filter dropped are not here.\nAbsent from the zero-record answers described under `stages`.","items":{}},"dataURIs":{"type":"array","description":"URIs for any files produced by the preview (e.g. file-based exports\nwriting to cloud storage). Empty for non-file adaptors.","items":{"type":"string"}},"dataRecordTraceKeys":{"type":"array","description":"Trace keys for the records in this preview — one per record,\ncorrelating stage outputs to their source record.","items":{"type":"string"}},"stages":{"type":"array","description":"Ordered list of pipeline stages the preview traversed. The preview\nruns the export's pipeline, not just the parse: `request` and `raw`\nfor HTTP exports; `parse` for a live read that returned records, and\nas the only stage when a `mockOutput` in the request body answers;\n`group` when the export groups records (grouped records are bare\narrays of the grouped rows). After a live `parse`, the stages the\nexport configures follow: the transform (`transformTwoDotZero` for\nTransformation 2.0 rules, `transform` for version 1 rules), the\noutput filter (`outputFilter`, where each record it drops carries\n`isIgnored: true`) and the preSavePage hook (`preSavePageHook`, which\nreceives only the records the filter kept). Top-level `data` holds\nthe records the last of them kept, unwrapped. Each stage carries its `data[]`\noutput and any `errors[]` raised at that stage. A live read that\nselects no records has been observed without a `parse` stage or\ntop-level `data` on two adaptors: an HTTP export returns only `request`\nand `raw`, and a NetSuite RESTlet search returns an empty object. Treat\nthose as zero records, not as a failed preview.","items":{"type":"object","description":"One pipeline stage's diagnostic and output envelope.","properties":{"name":{"type":"string","description":"Stage identifier (e.g. `request`, `raw`, `parse`, `transformTwoDotZero`, `outputFilter`, `preSavePageHook`)."},"data":{"description":"Stage output. Shape varies — `raw` carries the source\nresponse, `parse` (and `group`) carry the extracted records.\nThe transform and `outputFilter` stages carry one\n`{data, errors}` entry per record (a dropped record adds\n`isIgnored: true`), and `preSavePageHook` one entry for the\npage: the object the hook returned.","oneOf":[{"title":"Array","type":"array","items":{}},{"title":"Object","type":"object","additionalProperties":true},{"title":"Null","type":"null"}]},"errors":{"type":["array","null"],"description":"Errors raised at this stage, or `null` when clean.","items":{"type":"object","additionalProperties":true}}}}},"errors":{"type":"array","description":"Top-level error aggregate — typically mirrors the first non-null\n`stages[].errors[]` entry. Often omitted entirely when the preview\nran clean; callers should treat `undefined` and `[]` as equivalent.","items":{"type":"object","additionalProperties":true}},"traceKeysDuplicate":{"type":"array","description":"Trace keys that collided during the preview — a diagnostic for\ntrace-key generation; rarely non-empty in practice.","items":{"type":"string"}}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}}}},"paths":{"/v1/exports/preview":{"post":{"operationId":"previewExport","tags":["Exports"],"summary":"Preview the output of an export doc (no job created)","description":"Runs an export doc through the flow engine's preview pipeline and returns\nthe sample data it would have emitted, along with stage-by-stage\ndiagnostics and any errors encountered. **No Job record is created** and\nno flow-level state is updated — this is a stateless preview.\n\nBody is a complete export document (the shape you would POST to\n`/v1/exports`), typically without `_id`. The CLI uses it for two\nscenarios:\n- `celigo exports invoke` with a doc on stdin → ad-hoc preview of a\n  not-yet-saved export.\n- Agent-driven \"preview + refine\" loops where an LLM iterates on the\n  export config and calls this endpoint to sample output each time.\n\nThe scoped variant at\n`POST /v1/integrations/{_integrationId}/flows/{_flowId}/exports/preview`\ndoes the same thing but inherits flow + integration context (useful when\nthe export references flow-scoped settings). Prefer the unscoped variant\nwhen previewing a standalone export.\n\nUse `test.limit` inside the body to cap the number of records\nreturned. Configuration errors in the export doc surface in the\n`errors[]` and `stages[].errors[]` arrays within a 200 response, but\nnot every failure does: structural validation failures (e.g. missing\n`_connectionId`) return 4xx, and so does an output filter that can't\nread a record's value (see the 422 response).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Full export document (mirror of `POST /v1/exports` body shape),\noptionally with `test.limit` to cap preview record count. Omit\n`_id` when previewing a not-yet-saved export. For an export this\nendpoint previews (an HTTP export, for example), a `mockOutput` in\nthe body is returned as a lone `parse` stage instead of a live read,\nso remove it from a document read back with GET to sample the\nsource.","additionalProperties":true}}}},"responses":{"200":{"description":"Preview envelope for a preview that ran; a valid body can still answer\n422 (see that response). User-error in\nthe export config (handlebars template failures, runtime errors)\nsurfaces in `errors[]` and `stages[].errors[]`; inspect those before\ntrusting `stages[].data[]`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportPreviewResponse"}}}},"400":{"description":"The export type does not support preview. Virtual (unsaved) previews\nare rejected for `simple`, `webhook` and other virtual export types —\nonly builder-style exports (e.g. HTTP, RDBMS) can be previewed via\nthis endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"422":{"description":"Structural validation failed — typically `missing_required_field`\nfor fields the preview pipeline needs before it can execute\n(e.g. `_connectionId`). A valid output filter also fails the preview\nwith 422 when a record holds a value its type conversion can't read,\nsuch as an empty or non-date string under `epochtime`: the message is\n\"Invalid date. Cannot convert given string or number to date time.\",\nfollowed by `; record=` and that record as JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Preview export data

> Preview export data from a specific export within a flow. This endpoint allows you to\
> preview the data that would be exported, including the exported data, URIs to the data\
> in the source app, and processing stages information.<br>

````json
{"openapi":"3.2.0","info":{"title":"Exports","version":"1.0.0"},"tags":[{"name":"Exports","description":"Exports retrieve data from source systems — on a schedule, in delta mode, in real time,\non demand, or as a file/blob transfer — package the results into ≤ 5-MB pages, and pass\neach page to downstream flow steps. Depending on configuration, an export surfaces in the\nFlow Builder as an export, a real-time listener, a file transfer, or a mid-flow lookup.\n\n## Export schema\n\n{% openapi-schemas spec=\"export\" schemas=\"Export\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"PreviewRequest":{"type":"object","description":"Request body for previewing export data","allOf":[{"$ref":"#/components/schemas/Export"},{"type":"object","properties":{"postData":{"type":"object","description":"Delta window for this preview. Without it, a document preview renders\n`{{lastExportDateTime}}` and `{{currentExportDateTime}}` empty, so the preview doesn't\nreflect the export's delta window: an HTTP request gets empty strings, and an RDBMS\nquery gets empty, unquoted text, so a delta query fails to parse (observed on Snowflake;\n`delta.startDate` doesn't set them either). With it, an HTTP export inserts each value\nexactly as given, and an RDBMS query receives each wrapped in single quotes\n(`'2026-01-15T00:00:00.000Z'`, observed on Snowflake); other adaptors weren't checked.\nOn an export without `delta.dateFormat`, a real run renders both as ISO 8601 UTC with\nmilliseconds, so pass them in that form to preview the request such a run would send.","properties":{"currentExportDateTime":{"type":"string","description":"Value `{{currentExportDateTime}}` renders as in this preview (the window's end)."},"lastExportDateTime":{"type":"string","description":"Value `{{lastExportDateTime}}` renders as in this preview (the window's start)."}}}}}]},"Export":{"type":"object","required":["_id","name","adaptorType","createdAt","lastModified"],"description":"Export object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ExportBase"},{"$ref":"#/components/schemas/ResourceResponse"},{"$ref":"#/components/schemas/IAResourceResponse"},{"type":"object","properties":{"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"apim":{"$ref":"#/components/schemas/APIM"},"apiIdentifier":{"type":"string","readOnly":true,"description":"API identifier assigned to this export."},"asynchronous":{"type":"boolean","readOnly":true,"description":"Server-managed execution-mode flag set on creation."},"__linkedLookupCacheIds":{"type":"array","readOnly":true,"description":"Lookup caches linked to this export, managed by the platform.","items":{"type":"string","format":"objectId"}},"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the source resource this export was created from."},"_templateId":{"type":"string","format":"objectId","readOnly":true,"description":"Template this export was created from."},"draft":{"type":"boolean","readOnly":true,"description":"When true, this export is in draft state and has not been confirmed."},"draftExpiresAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the draft version of this export expires."},"debugUntil":{"type":"string","format":"date-time","readOnly":true,"description":"Turns on step debug capture until this time: while it lies in the future, every\nrequest this export sends is recorded in the step's debug log\n(`GET /v1/{flows|apis|tools}/{containerId}/{_id}/requests`). Read-only on PUT —\nwritten only by `PATCH /v1/exports/{_id}` on `/debugUntil`, at most 3600 seconds\nahead (`422` `update_not_allowed` beyond that); `remove` stops capture."}}}]},"ExportBase":{"type":"object","description":"Writable export fields shared by the request and response schemas.","properties":{"name":{"type":"string","maxLength":100,"description":"Display name for the export, shown in the flow builder, job history, and error logs.\nDescriptive, unique names indicating the data source and purpose make large accounts easier to manage."},"description":{"type":["string","null"],"description":"Free-text summary of what the export retrieves and why. Shown in the UI and available\nto AI agents for context; has no effect on execution.","maxLength":5120},"_connectionId":{"format":"objectId","type":["string","null"],"description":"Connection this export uses to reach the source system. The connection's type must be\ncompatible with the export's `adaptorType` (e.g. an `HTTPExport` needs an `http` connection;\n`NetSuiteExport` and `NetSuiteHTTPExport` both need a `netsuite` connection).\nServer-required unless `type` is `webhook` or `simple` (those receive data instead of\nfetching it) or `adaptorType` is `TopicExport` (Event Streams listeners are delivered\nby the platform itself and must omit it). A `WebhookExport` saved without `type: webhook`\nis refused with 422 \"_connectionId is required unless type is [webhook|simple]\"; a webhook\nexport that does carry a connection saves and updates normally."},"adaptorType":{"type":"string","description":"Selects the adaptor technology that executes this export, which determines the compatible\nconnection types and which adaptor-specific configuration object must also be supplied\n(e.g. set `salesforce` when using `SalesforceExport`).","enum":["HTTPExport","FTPExport","AS2Export","S3Export","NetSuiteExport","NetSuiteHTTPExport","SalesforceExport","JDBCExport","RDBMSExport","MongodbExport","DynamodbExport","WrapperExport","SimpleExport","WebhookExport","FileSystemExport","TopicExport","RESTExport"]},"nsDomainType":{"type":"string","enum":["suitetalk","restlet"],"description":"Selects which NetSuite REST host a `NetSuiteHTTPExport` calls. The server derives the\nfull base URL from the connection's NetSuite account\n(`https://<account>.suitetalk.api.netsuite.com` or `https://<account>.restlets.api.netsuite.com`)\nand signs each request with the connection's token-based credentials, so `http.relativeURI`\ncarries the complete path starting at `/services/rest/...` or `/app/site/hosting/restlet.nl`.\nIgnored on other adaptor types."},"type":{"type":["string","null"],"description":"Operational mode of the export. When omitted, the export retrieves all available records —\nthe standard batch mode, which is also the right choice for parsing structured files\n(CSV/XML/JSON) into records. Most modes take their matching configuration object (e.g. a\n`webhook` object when `type` is `webhook`); an HTTP or SQL `delta` export saves without a\n`delta` object because its request carries `{{lastExportDateTime}}`, though\n`delta.startDate` sets its first window. It still needs `type: delta`: on an export\nwith no `type`, `{{lastExportDateTime}}` and `{{currentExportDateTime}}` render\nempty on every run, so a request that carries them goes out with blank values each time\n(live-verified 2026-10-22). Use `blob` only to transfer raw files without parsing their\ncontents.","enum":["webhook","test","delta","once","tranlinedelta","simple","blob","distributed","stream","all"]},"pageSize":{"type":["integer","null"],"description":"Number of records per page streamed to downstream flow steps when the export is the\nflow's source step. Pages are additionally capped at 5 MB regardless of record count, so\nvery large records may produce smaller pages. The server does not validate the value.\nA lookup (`isLookup: true`) runs to completion and hands every result to the incoming\nrecord as one response, capped at 5 MB in total, so the value does not change how many\nresults a lookup returns; a result set larger than that belongs in a standalone export\nstep. In a test run or preview the value also caps the records fetched (default 20).\nOn a webhook listener the records of each incoming request are paged separately, before\nany hook or downstream step runs.","default":20},"dataURITemplate":{"type":"string","description":"Handlebars template that builds a link back to each record in the source application's UI\n(e.g. `https://my.salesforce.com/lightning/r/Contact/{{record.Id}}/view`). The resolved\nURL is stored with error records in job history so users can jump straight to the record."},"traceKeyTemplate":{"type":["string","null"],"description":"Handlebars template that overrides how each record's unique trace key is generated, used\nto track records through the flow and match retries to prior errors (e.g.\n`{{join \"_\" record.customerId record.orderId}}`). When omitted, the system picks a unique\nfield automatically from the record's top level, by exact field name (`GET\n/v1/flows/{_id}/traceKey` serves the pattern). The rendered value of a template is stamped\non each record as `io_custom_trace_key` and travels downstream with it. If a transform\nreshapes the data first, omit the `record.` prefix. Trace keys are capped at 256 characters;\na longer key is stored truncated from the middle, keeping the beginning and end of the\nvalue. Downstream imports use the trace key as their default concurrency lock key (a blank\nkey means no lock); it never de-duplicates — a record emitted twice with the same key is\nprocessed twice."},"skipRetries":{"type":"boolean","description":"When true, the platform does not retain the source data needed to retry failed records,\nreducing stored data at the cost of being unable to reprocess errors from this export.\nShown as \"Do not store retry data\" in the UI. Defaults to false."},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"isLookup":{"type":"boolean","description":"When true, the export runs as a mid-flow lookup: it executes once per incoming record,\nusing the input record's fields to parameterize the request, and passes the results to\nsubsequent steps. When false, the export runs as a standalone data source. A lookup's\nresults are not paged through the flow: every page the adaptor fetches is merged into one\nresponse for the record, up to 5 MB, so `pageSize` does not limit or extend what a lookup\nreturns. A lookup that must return more than that is better modeled as a standalone\nexport step."},"groupByFields":{"$ref":"#/components/schemas/GroupBy"},"delta":{"$ref":"#/components/schemas/Delta"},"test":{"$ref":"#/components/schemas/Test"},"once":{"$ref":"#/components/schemas/Once"},"webhook":{"$ref":"#/components/schemas/Webhook"},"simple":{"$ref":"#/components/schemas/Simple"},"distributed":{"$ref":"#/components/schemas/Distributed"},"cdc":{"$ref":"#/components/schemas/Cdc"},"topic":{"$ref":"#/components/schemas/Topic"},"filesystem":{"$ref":"#/components/schemas/FileSystem"},"http":{"$ref":"#/components/schemas/Http"},"file":{"$ref":"#/components/schemas/File"},"salesforce":{"$ref":"#/components/schemas/Salesforce"},"as2":{"$ref":"#/components/schemas/AS2"},"dynamodb":{"$ref":"#/components/schemas/DynamoDB"},"ftp":{"$ref":"#/components/schemas/FTP"},"jdbc":{"$ref":"#/components/schemas/JDBC"},"mongodb":{"$ref":"#/components/schemas/MongoDB"},"netsuite":{"$ref":"#/components/schemas/NetSuite"},"rdbms":{"$ref":"#/components/schemas/RDBMS"},"s3":{"$ref":"#/components/schemas/S3"},"wrapper":{"$ref":"#/components/schemas/Wrapper"},"parsers":{"$ref":"#/components/schemas/Parsers"},"filter":{"description":"Filter applied to each record after the export's transform and before its preSavePage\nhook. Records that match continue through the flow; records that don't are silently\ndropped. Filter expressions reference the transformed record's fields.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"inputFilter":{"description":"Filter applied to incoming records before a lookup export queries the external system.\nOnly matching input records trigger lookup calls; non-matching records pass through the\nstep unenriched, which cuts unnecessary API traffic. Only relevant when `isLookup` is true.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"mappings":{"description":"Field mappings applied to each incoming record before a lookup HTTP request is made,\nreshaping the upstream record into the structure the lookup target API expects. Only\nsupported when `isLookup` is true and `adaptorType` is `HTTPExport` or\n`NetSuiteHTTPExport`; do not set it on source exports or other lookup adaptors.","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"transform":{"description":"Transformation that reshapes records before they leave this step — source exports\ntransform the retrieved records, lookup exports transform the lookup results. Commonly\nused to flatten nested lookup responses (e.g. `results[0].id` → `id`); merging results\nback into source records is handled separately by the flow's response mapping.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the export lifecycle for\ntransformations, validation, and filtering beyond what configuration alone can express.\nA hook name not listed here (an import's `preMap`, for example) is dropped on save\nwithout an error, so read `hooks` back after saving.","properties":{"preSavePage":{"type":"object","description":"Hook that runs on each page of records after the export's transform and output\nfilter, before the page is sent to downstream steps; commonly used to flatten\nstructures, drop unwanted records, or add computed fields. It receives the page as\n`options.data` (with `errors`, `pageIndex`, `configuration` and `settings`) and returns\n`{data, errors, abort, newErrorsAndRetryData}`. Return that object, not the array: a\nhook that returns `options.data` itself, or a filtered copy, drops the page without an\nerror, and the run completes with 0 records, 0 errors and 0 pages. An exception thrown\nhere fails every record on that page, each with its own retry data, and later pages\nstill run.","properties":{"function":{"type":"string","description":"Function to invoke within the referenced script."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`. Must be a script in this\naccount: any other id fails the save with 422 (\"Only a script owned by the user or\na script shared by the Integration App can be used at hooks.preSavePage._scriptId.\")."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments."},"configuration":{"type":"object","description":"Static parameters passed to the hook function at runtime, letting one script be\nreused across exports with different settings."}}},"preParse":{"type":"object","description":"Hook that runs on file-based exports after the file is downloaded and before it is\nparsed: it receives the raw file text as `preParseData` together with `fileMeta` and\nreturns both, modified or not, for the configured parser to read. Used to normalize a\nnon-standard file — fix a header row, strip a preamble, convert a format — before\nparsing; an exception thrown here fails the run.","properties":{"function":{"type":"string","description":"Function to invoke within the referenced script."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments."},"configuration":{"type":"object","description":"Static parameters passed to the hook function at runtime, letting one script be\nreused across exports with different settings."}}}}},"settingsForm":{"$ref":"#/components/schemas/Form"},"settings":{"$ref":"#/components/schemas/Settings"},"mockOutput":{"$ref":"#/components/schemas/MockOutput"},"_ediProfileId":{"type":"string","format":"objectId","description":"EDI profile this export uses to parse incoming X12 or EDIFACT documents into structured\nJSON — it supplies the envelope qualifiers, delimiters, version, and validation rules.\nSet it when the export parses or validates EDI files (a save with a `delimited/x12` file\ndefinition at `version` `\"2\"` and no profile fails with 422 `missing_ediProfile`); omit\nit otherwise. Accepted on the file-reading exports — FTP, AS2, S3 and HTTP file mode\n(`http.type: \"file\"`)."},"_postParseListenerId":{"type":"string","format":"objectId","description":"Webhook export invoked once per file after EDI parsing, on both success and failure\n(errors are included in the payload when parsing fails). Primarily used to send\nfunctional acknowledgements (997/999) to trading partners. Accepted on the file-reading\nexports that parse EDI — FTP, AS2, S3 and HTTP file mode (`http.type: \"file\"`)."},"externalId":{"type":["string","null"],"description":"External identifier for correlating the export with a record in another system."},"_integrationId":{"type":["string","null"],"format":"objectId","description":"Not stored on an export: a value sent on create or on a full update is dropped, and a GET\ndoesn't return the field. An export isn't owned by an integration; to find the flows and\nother resources that use it, call `GET /v1/exports/{_id}/dependencies`."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this export was created from, set when the export is part of an installed\nintegration app. The app's install sets it: a create that sends it is refused with 403\n(\"Cannot create an Integration App resource\")."},"unencrypted":{"type":"object","description":"Custom configuration values stored without encryption and returned in API responses."},"useTechAdaptorForm":{"type":"boolean","description":"When true, the UI presents the full technical adaptor form for this export instead of\nthe simplified assistant form."},"rawData":{"type":"string","description":"Key referencing the raw sample payload captured for preview and testing."},"preSave":{"$ref":"#/components/schemas/PreSave"},"assistant":{"type":"string","description":"Identifier for the connector assistant used to configure this export. For the cloud\nfile providers it also selects the provider-specific location rules —\n`azuredatalakestoragegen2` (container in `file.directory.name`, path in\n`http.relativeURI`) and `googlecloudstorage` (bucket in `file.directory.storageRootId`,\nprefix in `file.directory.id`)."},"assistantMetadata":{"type":["object","string"],"additionalProperties":true,"description":"Metadata associated with the connector assistant configuration."},"sampleData":{"type":["string","object","array","null"],"description":"Sample data payload used for previewing and testing the export."},"sampleHeaders":{"type":"array","description":"Sample HTTP headers used for previewing and testing the export.","items":{"type":"object","properties":{"name":{"type":"string","description":"Header name."},"value":{"type":"string","description":"Header value."}}}},"sampleQueryParams":{"type":"array","description":"Sample query parameters used for previewing and testing the export.","items":{"type":"object","properties":{"name":{"type":"string","description":"Query parameter name."},"value":{"type":"string","description":"Query parameter value."}}}}}},"OneToMany":{"type":"boolean","description":"When true, the step runs once per child record instead of once per incoming record.\n`pathToMany` names the array field that holds the children inside an object record; when\nthe incoming record is itself an array (grouped or row-based data), leave `pathToMany` blank\nand each element becomes a record. The fan-out is scoped to this step: afterwards the children\nare re-joined into the record's original shape — the object with its array, or the array of rows —\ncarrying any response-mapping enrichment, and that re-joined record is what the next step\nreceives. Not for locating records in an export's HTTP response; use\n`http.response.resourcePath` for that. Applies to steps that receive a record — imports and\nlookups (an export referenced as a page processor of a flow, API, or Tool). A standalone export\n(a flow's page generator) has no incoming record, so the fields are saved but have no effect on\nthe records it produces; to emit one record per array element from a standalone export, use a\n`hooks.preSavePage` script, or for a file-definition export make the repeating segment the\nrecord boundary in the definition's rules.\n","default":false},"PathToMany":{"type":"string","description":"Path to the array of child records inside an object record when `oneToMany` is true, in dot\nnotation (`items`, `lines.lineItems`). Leave blank when the incoming record is itself an array\n(grouped or row-based data) — each element is then a child record. A path that does not resolve\nto an array processes zero records and reports success. Read only on steps that receive a\nrecord (imports, lookup exports); on a standalone export it is saved and ignored — see `oneToMany`.\n"},"GroupBy":{"type":"array","description":"Specifies which fields to use for grouping records in the export results. When configured, records with\nthe same values in these fields will be grouped together and treated as a single record by downstream\nsteps in your flow.\n\nFor example:\n- Group sales orders by customer ID to process all orders for each customer together\n- Group journal entries by accounting period to consolidate related transactions\n- Group inventory items by location to process inventory by warehouse\n\nWhen grouping is used, the export's page size determines the maximum number of groups per page, not individual\nrecords. Note that effective grouping typically requires that records with the same group field values appear\ntogether in the export data.\n\nNot a webhook listener feature: on a `webhook` export the platform applies it only within each page, after\nthe request has been paged, and only to adjacent rows — the field is not offered for listeners in the UI.\n","items":{"type":"string"}},"Delta":{"type":"object","description":"Configures incremental exports that retrieve only records created or modified since the\nflow's delta checkpoint; omit it for other export types. Adaptors that compare a record field\nname it in `dateField`, while HTTP exports and SQL queries carry {{lastExportDateTime}} in the\nrequest and save without a delta object. Each run's window runs from {{lastExportDateTime}}\n(the checkpoint) to {{currentExportDateTime}} (the time the run started). A first run is not a\nfull export: with no `startDate`, its {{lastExportDateTime}} is the run's own timestamp, so it\npicks up no earlier changes. The checkpoint moves to a run's window end only when the run\nexports records, so a run that exports nothing leaves it where it was, and after a failed run\nthe next run reuses the last checkpoint. On success, when `POST /v1/flows/{_id}/run` sends\n`export.endDate`, the flow's checkpoint is left at that value, so an end date in the past\nmoves the next run's lower bound back.","properties":{"dateField":{"type":"string","description":"Record timestamp field(s) compared against the last successful run time to identify\nchanged records. Accepts a single field or multiple comma-separated fields, processed\nsequentially — useful when different operations update different timestamp fields.\nIf the flow's own downstream steps update the exported records, add export criteria\nthat exclude already-processed records (or use a creation-time field for process-once\nflows) so each run doesn't re-export what the previous run wrote. Not supported on\nHTTP exports: embed {{lastExportDateTime}} in the relativeURI or body instead; an HTTP\nexport saved with dateField is refused with 422 \"Unsupported dateField in\nexport.delta.dateField\". For Salesforce this\nfield is required and defaults to the standard timestamp fields (LastModifiedDate,\nCreatedDate, SystemModstamp, LastActivityDate, LastViewedDate, LastReferencedDate) plus\nany custom timestamp fields."},"dateFormat":{"type":"string","description":"Moment.js format string applied to both delta variables, {{lastExportDateTime}} and\n{{currentExportDateTime}}, in HTTP requests and SQL queries alike. Leave unset unless the\nsource system requires a non-ISO8601 format; by default both render as ISO 8601 UTC with\nmilliseconds. Date-only formats truncate the time portion, widening the filter window."},"lagOffset":{"type":"integer","description":"Buffer in milliseconds subtracted from the last successful run timestamp, creating an\noverlapping window that catches records still propagating when the previous run executed.\nSet it when records created or modified near the run time are occasionally skipped due to\nreplication or indexing delays. Keep it as low as possible — larger values reprocess\nredundant records. A negative value shifts the window forward (look-ahead) instead of back."},"startDate":{"type":["string","null"],"format":"date-time","description":"Lower-bound cutoff for the first run. Omitting it doesn't export history: the first run\nthen starts at its own timestamp, an empty window. Set it to the earliest change the first\nrun should pick up. Later runs start from the flow's checkpoint, the end of the last run\nthat exported records."}}},"Test":{"type":"object","description":"Configures test exports that cap the number of records retrieved, for safely developing and\nvalidating against small data samples. Required when the export's type is \"test\"; omit for\nother export types. A test export behaves like a standard export in every other way (filters,\npagination, processing) and stores no state between runs.","properties":{"limit":{"type":"integer","default":1,"description":"Caps the total records a test run processes, counted on top-level records before\noneToMany processing; the maximum exists to prevent accidentally processing large\ndatasets during development. The cap applies across pages — processing stops once the\nlimit is reached regardless of pageSize. When transitioning to production, leave this in\nplace and change the export's type field; the limit then no longer applies.","minimum":1,"maximum":100}}},"Once":{"type":"object","description":"Configures flag-based exports that skip records an earlier run already processed. Required\nwhen the export's type is \"once\"; omit for other export types. Each run retrieves records\nwhere the tracking boolean field is false, then sets that field to true after successful\nprocessing so later runs skip them; if a run fails, the flags are left unchanged and the\nrecords are retried automatically on the next run.","properties":{"booleanField":{"type":"string","description":"API field name of the boolean/checkbox in the source system that tracks processed\nrecords: the export selects only records where this field is false, then sets it to true\nin batches after each successfully processed page. The field must be writeable by the\nexport's connection. Ensure no other process updates the same field — use a separate\nflag per export process — or records may be skipped unexpectedly."}}},"Webhook":{"type":"object","description":"Configuration for webhook listeners that receive data through incoming HTTP requests. Required when the export's `type` is `webhook`; omit for other export types. Senders POST to the export's listener URL, `https://api.integrator.io/v1/exports/{_id}/data` (or the same path on the account's regional API host); the platform validates each request using the configured `verify` method, passes the payload to subsequent flow steps, and returns a configurable HTTP response to the caller. A request is answered 204 by default when accepted, 401 when it fails verification, and 403 `unable_to_run_flows` while every flow the export feeds is disabled; a refused request is not queued, so the sender must resend it.","properties":{"provider":{"type":"string","description":"Source application sending the webhook, used to pre-configure security settings and payload parsing for that platform. Use `custom` (the default when omitted) for unlisted providers or when you need full manual control over the security configuration. Provider-specific choices may require credentials (tokens, keys) mandated by that platform.","enum":["github","shopify","travis","travis-org","slack","dropbox","onfleet","helpscout","errorception","box","stripe","aha","jira","pagerduty","postmark","mailchimp","intercom","activecampaign","segment","recurly","shipwire","surveymonkey","parseur","mailparser-io","hubspot","integrator-extension","custom","sapariba","happyreturns","typeform"]},"verify":{"type":"string","description":"Verification method applied to every incoming request before processing; required for all webhook exports. Each method needs companion fields: `hmac` requires `key`, `algorithm`, `encoding`, and `header` (except on connector-backed webhooks, where the connector definition supplies them); `token` requires `token`, with `tokenLocation` defaulting to `body` and selecting which location-specific field applies; `basic` requires `username` and `password`; `secret_url` requires `token`. Prefer `hmac` when the source system supports it — it is the most secure option; `secret_url` relies only on URL obscurity and suits non-sensitive data or testing.","enum":["token","hmac","basic","secret_url"]},"token":{"type":"string","description":"Shared secret used when `verify` is `token` or `secret_url`. For `token` verification, the value found at `path` in each request must exactly match (case- and whitespace-sensitive) or the request is rejected with a 401. For `secret_url`, the token is embedded in the webhook URL to create a hard-to-guess endpoint — generate a random, high-entropy value and treat it as a sensitive credential."},"algorithm":{"type":"string","description":"Hashing algorithm used to validate HMAC signatures when `verify` is `hmac`. Must match the algorithm the webhook sender uses — a mismatch causes every request to be rejected. Use `sha256` unless the provider explicitly requires another value.","enum":["sha1","sha256","sha384","sha512"]},"encoding":{"type":"string","description":"Encoding of the HMAC signature value when `verify` is `hmac`. Must match the encoding the webhook sender uses — a mismatch causes requests to be rejected with a 401 even when the signature is otherwise correct. With `provider: custom`, `hex` is compared as lowercase hex: an uppercase hex signature is rejected.","enum":["hex","base64"]},"key":{"type":"string","description":"Secret used to validate signatures when `verify` is `hmac`. The system computes a signature of the request body using this key and the configured `algorithm`, then compares it with the signature sent in the request header named by `header`. Treat it as a highly sensitive credential — never expose it in repositories or logs."},"header":{"type":"string","description":"Name of the request header that carries the HMAC signature when `verify` is `hmac`. Must match the header name the webhook sender uses (header names are case-insensitive); requests without this header are rejected with a 401. With `provider: custom` the header value must be the bare digest in the configured `encoding`: a value with a `sha256=` prefix is rejected with a 401."},"tokenLocation":{"type":"string","enum":["body","header","queryParam"],"default":"body","description":"Where the verification token is found when `verify` is `token`. Each location uses a different companion field: `body` uses `path`, `header` uses `tokenHeaderName` (and `tokenHeaderScheme`), `queryParam` uses `tokenQueryParamName`."},"path":{"type":"string","description":"JSON path into the request body holding the verification token when `verify` is `token` and `tokenLocation` is `body` (e.g. `meta.token`). The value at this path must exactly match `token` or the request is rejected."},"tokenHeaderName":{"type":"string","description":"Request header carrying the verification token when `verify` is `token` and `tokenLocation` is `header`."},"tokenHeaderScheme":{"type":"string","enum":["bearer","custom","none"],"default":"bearer","description":"Scheme prefixing the token in the header when `tokenLocation` is `header`."},"customTokenScheme":{"type":"string","description":"Custom header scheme prefix used when `tokenHeaderScheme` is `custom`."},"tokenQueryParamName":{"type":"string","description":"Query parameter carrying the verification token when `verify` is `token` and `tokenLocation` is `queryParam`."},"_httpConnectorId":{"type":"string","format":"objectId","description":"HTTP connector backing this listener when the webhook is provided by an assistant/connector (e.g. Slack); set by the platform for connector-backed listeners."},"requestMediaType":{"type":"string","enum":["json","xml","csv","urlencoded","plaintext"],"description":"Overrides how the incoming request body is parsed when the provider sends a non-standard content type. When omitted, the body is parsed by its Content-Type header."},"pathToRecords":{"type":"string","description":"JSON path into an object payload to the array whose elements become the records. When omitted, a top-level JSON array is emitted as one record per element and an object payload is emitted as a single record."},"includeParentData":{"type":"boolean","description":"When true and `pathToRecords` is set, each emitted record also carries the surrounding parent fields from the payload."},"username":{"type":"string","description":"Username half of the credentials when `verify` is `basic`. Incoming requests must include an `Authorization: Basic` header carrying the base64-encoded `username:password` pair. Use only over HTTPS to prevent credential interception."},"password":{"type":"string","description":"Password half of the credentials when `verify` is `basic`, validated together with `username` from the request's `Authorization` header. Use a strong, unique value and treat it as a sensitive credential."},"successStatusCode":{"type":"integer","description":"HTTP status code returned to the caller after successful processing; must be a valid 2xx code. The default 204 returns no response body and causes `successBody` to be ignored — set 200 or 202 when the caller needs a response body or expects a specific code.","default":204},"successBody":{"type":"string","description":"Response body returned to the caller after successful processing; ignored when `successStatusCode` is 204. Content type is set by `successMediaType`, and the value can be static text or structured JSON/XML, including handlebars expressions for dynamic values."},"successMediaType":{"type":"string","description":"Sets the Content-Type header on successful webhook responses. Only takes effect when a `successBody` is returned (a status code other than 204), and must match the actual format of that body.","default":"json","enum":["json","xml","plaintext"]},"successResponseHeaders":{"type":"array","description":"Custom headers added to successful webhook responses — for example CORS headers or correlation IDs. Headers defined here take precedence over automatically set headers such as Content-Type, and values support handlebars expressions for dynamic content.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the header to set on successful webhook responses; headers defined here take precedence over automatically set headers such as Content-Type."},"value":{"type":"string","description":"Value sent for the header; supports handlebars expressions for dynamic content."}}}},"challengeResponseHeaders":{"type":"array","description":"Custom headers returned for webhook subscription verification (challenge) requests, which providers send before delivering real events. Required header values vary by provider — consult the provider's documentation, since incorrect challenge headers can prevent the subscription from being verified.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the header to set on challenge (subscription verification) responses."},"value":{"type":"string","description":"Value sent for the header; consult the provider's documentation for required challenge header values."}}}},"challengeSuccessBody":{"type":"string","description":"Response body returned for webhook subscription verification (challenge) requests. Many providers require echoing back a challenge value from the request, which handlebars expressions can access — for example `{{hub.challenge}}` (Facebook/Instagram) or `{\"challenge\":\"{{challenge}}\"}` (Slack). An incorrect challenge response prevents the subscription from being verified."},"challengeSuccessStatusCode":{"type":"integer","description":"HTTP status code returned for webhook subscription verification (challenge) requests. Most providers expect the default 200; change it only when the provider's verification explicitly requires a different code.","default":200},"challengeSuccessMediaType":{"type":"string","description":"Sets the Content-Type header on challenge responses. Must match both the format of `challengeSuccessBody` and the provider's requirements — for example, Slack expects `json` while Facebook/Instagram verification expects `plaintext`.","default":"json","enum":["json","xml","plaintext"]}},"if":{"properties":{"verify":{"const":"hmac"}},"required":["verify"],"not":{"required":["_httpConnectorId"]}},"then":{"required":["key","algorithm","encoding","header"]},"else":{"if":{"properties":{"verify":{"const":"token"}},"required":["verify"]},"then":{"required":["token"],"if":{"properties":{"tokenLocation":{"const":"body"}},"required":["tokenLocation"]},"then":{"required":["path"]},"else":{"if":{"properties":{"tokenLocation":{"const":"header"}},"required":["tokenLocation"]},"then":{"required":["tokenHeaderName"]},"else":{"if":{"properties":{"tokenLocation":{"const":"queryParam"}},"required":["tokenLocation"]},"then":{"required":["tokenQueryParamName"]},"else":{"required":["path"]}}}},"else":{"if":{"properties":{"verify":{"const":"basic"}},"required":["verify"]},"then":{"required":["username","password"]},"else":{"if":{"properties":{"verify":{"const":"secret_url"}},"required":["verify"]},"then":{"required":["token"]}}}}},"Simple":{"type":"object","description":"Configuration for `simple` (data-loader) exports — exports that accept manually uploaded\nfiles through the data-loader UI instead of fetching data from a connection. Relevant only\nwhen the export's `type` is `simple`; the uploaded content is parsed using the same file\nsettings as file-based exports.","properties":{"file":{"$ref":"#/components/schemas/File"}}},"File":{"type":"object","description":"Controls how files are parsed, filtered, and processed across all file-based exports\n(FTP/SFTP, Amazon S3, simple file uploads, and other file sources). The type field selects\nthe file format and determines which format-specific object (csv, json, xlsx, xml, or\nfileDefinition) must be configured; the output field selects whether files are parsed into\nrecords, transferred as blobs, or listed as metadata only. The filter object selectively\nskips files before processing.","properties":{"encoding":{"type":"string","description":"Character encoding used to read and parse file content. If the encoding is unknown, try\nutf8 first (the default), then win1252 for Western-language files with garbled\ncharacters; consider the geographic origin of the data when selecting.","enum":["utf8","win1252","utf-16le","gb18030","macroman","iso88591","shiftjis"]},"type":{"type":"string","description":"Format of the files being processed; determines which format-specific configuration\nobject (csv, json, xlsx, xml, or fileDefinition) must be populated — other format\nobjects are ignored. Required for all file-based exports except blob exports (export\ntype \"blob\" or output \"blobKeys\").","enum":["csv","json","xlsx","xml","filedefinition"]},"output":{"type":"string","description":"Processing mode for retrieved files: parse contents into records, transfer files as\nunparsed blobs, or return only file metadata. Determines what data is passed to\nsubsequent flow steps.","enum":["records","metadata","blobKeys"]},"skipDelete":{"type":"boolean","default":false,"description":"When true, source files remain on the file server after processing; when false (the\ndefault), files are deleted after successful processing. Files that fail processing are\nnever deleted, regardless of this setting. Enable retention when files must be processed\nby other flows or systems, or kept for compliance."},"compressionFormat":{"type":"string","description":"Compression format of incoming files, which are decompressed before any other processing\n(parsing, filtering). Set this only when the source always delivers compressed files —\nif a file marked as compressed is not actually compressed, processing fails. Leave unset\nwhen files arrive uncompressed or only sometimes compressed.","enum":["gzip","zip"]},"purgeInternalBackup":{"type":"boolean","description":"When true, Celigo keeps no internal backup copies of files processed by this export;\nwhen false (the default), copies are retained for your account's retention period and\nare available for reprocessing or troubleshooting. Applies only to this export and only\nto Celigo's internal copies — source files are governed by skipDelete. Enable for highly\nsensitive data or zero-retention policies; without backups, recovery may require\nre-obtaining files from the source system."},"decrypt":{"type":"string","description":"Decryption applied to incoming files before any other processing; decryption runs before\ndecompression, and a decryption failure fails the file's processing entirely. The\nconnection must already be configured with the private key (and passphrase, if\napplicable) matching the public key used to encrypt the files. Only PGP/GPG encryption\nis currently supported.","enum":["pgp"]},"batchSize":{"type":["integer","null"],"description":"Number of files retrieved per batch; if a batch fails, the whole batch is retried\n(stored as `null` when not configured). Use\nlower values (10-50) for large files to reduce timeout and memory pressure, and higher\nvalues for many small files to improve throughput. Controls file retrieval only — record\npaging is governed by the export's pageSize.","maximum":1000},"sortByFields":{"type":"array","description":"Sorts the records parsed from each file before they are processed, establishing a\ndeterministic processing order (for example, chronological or priority-based). Sorting\nhappens after parsing but before any filtering or grouping, and does not modify the\nsource files. Sorting by the same fields used in groupByFields improves grouping\nperformance.","items":{"type":"object","properties":{"field":{"type":"string","description":"Record field to sort by; use dot notation for nested properties (e.g.\ncustomer.name). Field names are case-sensitive."},"descending":{"type":"boolean","description":"When true, sorts this field in descending order (newest/highest first); when false\nor omitted, sorts ascending. Directions can be mixed across fields in a\nmulti-field sort."}}}},"groupByFields":{"$ref":"#/components/schemas/GroupBy"},"groupEmptyValues":{"type":"boolean","description":"When true, records whose groupByFields values are empty are still grouped together rather\nthan each forming its own group; when false (the default), empty-keyed records are not\ngrouped. Only relevant when groupByFields is set."},"csv":{"type":"object","description":"Parsing settings for delimiter-separated text files (CSV, TSV, pipe-delimited, and\nsimilar). Configure when type is \"csv\".\n\nRequired when type is csv.","properties":{"columnDelimiter":{"type":"string","description":"Character sequence separating fields within each row; comma when omitted. Use \"\\t\"\nfor tab-delimited files; European-locale exports often use semicolons. A wrong\ndelimiter doesn't fail the parse: with a header row, each row becomes one field named\nafter the whole header line (`{\"order_id;sku;qty\": \"1001;TEE-M;2\"}`), so the failure\nshows up in later steps."},"rowDelimiter":{"type":"string","description":"Character sequence marking the end of each record; auto-detected from file content\nwhen omitted. Set explicitly (\"\\n\", \"\\r\\n\", or \"\\r\") only when auto-detection fails,\nsuch as for files with mixed line endings — an incorrect value merges or splits\nrecords."},"hasHeaderRow":{"type":"boolean","description":"When true (the default), the first row is read as field names rather than data, and\nthose names are available in mappings. When false, every row is treated as a data\nrecord and fields are referenced by position."},"trimSpaces":{"type":"boolean","description":"When true, removes leading and trailing whitespace from every field value during\nparsing; when false (the default), whitespace is preserved exactly as in the source.\nHeader row values are always trimmed regardless of this setting, and spaces between\nwords are never affected."},"rowsToSkip":{"type":"integer","description":"Number of rows at the top of the file to ignore before parsing begins — useful for\nreport titles, timestamps, or other metadata above the data. The header row (when\nhasHeaderRow is true) is expected after the skipped rows."},"disableQuoteAndStripEnclosingQuotes":{"type":"boolean","description":"When true, disables CSV quote processing: quotes are treated as literal characters,\nenclosing quotes are stripped, and delimiters inside quoted text split the field.\nWhen false (the default), standard RFC 4180 quoting applies and quoted fields\nprotect embedded delimiters. Enable only for files with non-standard or malformed\nquoting; review this setting first when field counts vary unexpectedly between rows."},"keyColumns":{"type":"array","description":"Field names whose values together identify a record's group when consecutive rows\nshare the same key — used to merge multi-line records (e.g. an order header repeated\nacross line-item rows) into a single record. Leave empty for one record per row; the\ncolumns must exist in the parsed header.","items":{"type":"string"}}}},"json":{"type":"object","description":"Parsing settings for JSON files. Configure when type is \"json\". resourcePath locates the\narray of records when they are nested inside a container object; malformed JSON fails\nthe entire file's processing.\n\nRequired when type is json.","properties":{"resourcePath":{"type":"string","description":"Dot-notation path to the array of records within the JSON structure (e.g.\n\"response.data.customers\"); leave empty when the file's root is already the record\narray. The path must resolve to an array of objects — array indexing and wildcards\nare not supported. A path that resolves to nothing produces zero records without\nraising an error."}}},"xlsx":{"type":"object","description":"Parsing settings for Microsoft Excel (.xlsx) workbooks. Configure when type is \"xlsx\".\nCalculated cell values are extracted rather than formulas; legacy .xls files are not\nsupported — use the modern Open XML format.\n\nRequired when type is xlsx.","properties":{"hasHeaderRow":{"type":"boolean","description":"When true (the default), the first row is read as field names rather than data;\nblank header cells are auto-named and duplicate names are made unique with suffixes.\nWhen false, every row is treated as data and fields receive generic positional names\n(Column1, Column2, ...)."}}},"xml":{"type":"object","description":"Parsing settings for XML documents. Configure when type is \"xml\". resourcePath is an\nXPath expression selecting the elements treated as records; namespaces are handled\nautomatically.\n\nRequired when type is xml.","properties":{"resourcePath":{"type":"string","description":"XPath expression selecting the elements treated as records — each matching element\nbecomes one record, with its child elements as fields. Required for XML parsing;\nthere is no default. Use an absolute path (/Root/Order) when the structure is fixed,\n//Element to match at any depth, or predicates (//Element[@type=\"product\"]) to\nfilter; XPath is case-sensitive, and a non-matching path yields zero records."}}},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"},"fileDefinition":{"type":"object","description":"Parsing via a predefined file definition resource, for formats the standard parsers\ncannot handle — fixed-width files, EDI documents (X12, EDIFACT), and multi-record-type\nor proprietary formats. Configure when type is \"filedefinition\".\n\nRequired when type is filedefinition.","properties":{"_fileDefinitionId":{"type":"string","format":"objectId","description":"File definition resource containing the parsing rules for this format. Must\nreference an existing, accessible file definition — never guess or fabricate the ID.\nDefinitions are reusable across exports, so changing one affects every export that\nuses it."},"allowPartialSuccess":{"type":"boolean","description":"When true, a file whose records partially fail parsing against the file definition\nstill emits the records that did parse, instead of failing the entire file. Applies to\nfile-definition (fixed-width, EDI) parsing only. For X12 and EDIFACT the unit is the\ntransaction set (ST–SE, UNH–UNT), and it takes effect only with an EDI profile linked:\na failed set is set aside and reported as an error while the file's other sets go on\nas records. Without this flag, one failed set fails the whole file."}}},"filter":{"description":"Selects which files are processed: files matching the filter criteria are exported,\nand non-matching files are skipped entirely before processing begins. Filterable\nfields come from each file's listing entry: the name as `fileName`, the size as\n`filesize`, and the modified time as `lastModifiedTime` on FTP, SFTP and S3 or\n`modifiedTime` on cloud-storage folders (`http.type: \"file\"`)\n(e.g. [\"endswith\", [\"string\", [\"extract\", \"fileName\"]], \".csv\"]). There is no\n`lastmodified` key.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"backupPath":{"type":"string","description":"Path where backup copies of source files are stored before processing."},"includeSubdirectories":{"type":"boolean","default":false,"description":"When true, an Azure Data Lake Storage Gen2 transfer (`assistant: azuredatalakestoragegen2`,\n`http.type: file`) also picks up files in the subdirectories below the directory path in\n`http.relativeURI`; when false or omitted only that directory's own files are transferred."},"directory":{"type":"object","required":["pathMode"],"description":"Structured source location for cloud file-provider sources (Google Drive shared drives,\nBox, Dropbox, Google Cloud Storage, Azure Data Lake Storage Gen2), replacing the flat path\nused by classic FTP/S3 sources. Select the location by folder ID or by a path relative to\na configured storage root via pathMode. For the drive-style providers, directoryId mode\naddresses the folder by id alone — the flat relative-path field is not consulted at\nruntime. Google Cloud Storage puts the bucket in `storageRootId` and the object prefix in\n`id` with `pathMode: directoryId`; Azure Data Lake Storage Gen2 puts the container in\n`name` and the directory path in `http.relativeURI`.","properties":{"pathMode":{"type":"string","enum":["relativePath","directoryId"],"description":"How the directory is addressed — by provider folder ID, or by a path relative to a configured storage root."},"id":{"type":"string","description":"Provider-native folder ID of the source directory (e.g. a Google Drive folder ID); set\nwhen pathMode is directoryId. This is the file provider's own ID, not a Celigo resource ID.\nFor Google Cloud Storage it is the object prefix inside the bucket, ending with `/`."},"name":{"type":"string","description":"Display name of the folder identified by id, kept for readability in the UI; the\nfolder browser fills it when the location is picked visually. For Azure Data Lake\nStorage Gen2 it is the authoritative container name instead — the form saves the\ncontainer here and no id."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root (e.g. a Google Drive shared-drive ID) the relative\npath resolves against; set when pathMode is relativePath. The provider's own ID, not a Celigo ID.\nFor Google Cloud Storage it is the bucket name."},"storageRootName":{"type":"string","description":"Display name of the storage root identified by storageRootId, retained for reference in the UI."}}},"backupDirectory":{"type":"object","required":["pathMode"],"description":"Structured destination for backup copies of source files on cloud file providers — the\nfile-provider counterpart to backupPath. Addressed the same way as directory.","properties":{"pathMode":{"type":"string","enum":["relativePath","directoryId"],"description":"How the backup location is addressed — by provider folder ID, or by a path relative to a configured storage root."},"id":{"type":"string","description":"Provider-native folder ID of the backup directory; set when pathMode is directoryId."},"name":{"type":"string","description":"Display name of the backup folder identified by id, kept for readability in the UI;\nthe folder browser fills it when the location is picked visually."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root the backup relative path resolves against; set when pathMode is relativePath."}}}}},"IncludeParentData":{"type":"boolean","description":"When true and a path to records is configured on the export (`pathToRecords` for MongoDB and DynamoDB, `http.response.resourcePath` for HTTP, `file.json`/`file.xml` `resourcePath` for file-based exports), the data outside that path is attached to each extracted record as a `_PARENT` object, keeping the surrounding envelope available to downstream steps. When omitted or false, records are emitted without `_PARENT`, exactly as before. Constraints: on HTTP exports this option cannot be combined with `useXpathParser: true` (rejected at save with `EXPORT_INCLUDE_PARENT_DATA_XPATH_UNSUPPORTED`), and XML responses must resolve to the default converted-JSON parse mode — a run whose XML parser resolves to DOM/XPath mode (for example, because a connection-level field uses an advanced XPath expression) fails at start with the same error rather than silently omitting `_PARENT`. On file-based exports, it requires `file.type` of `json` with `file.json.resourcePath` configured — XML file envelope capture is not yet supported (the streaming XML parser emits no envelope event; XML support arrives with a parsers-package enhancement) — and any other file configuration is rejected at save with `EXPORT_INCLUDE_PARENT_DATA_UNSUPPORTED_FILE_TYPE`."},"Filter":{"type":"object","description":"Configuration for selectively processing records based on specified criteria. This object enables\nprecise control over which items are included or excluded from processing operations.\n\n**Filter behavior**\n\nWhen configured, the filter is applied before processing begins:\n- Items that match the filter criteria are processed\n- Items that don't match are completely skipped\n- No partial processing is performed\n\nSend the whole object. A rules array sent alone as the `filter` value is not\nstored, and a full update of an export that carries one removes the filter the\nexport had, with no error.\n\n**Implementation approaches**\n\nThere are two distinct filtering mechanisms available:\n\n**Rule-Based Filtering (`type: \"expression\"`)**\n- **Best For**: Common filtering patterns based on standard attributes\n- **Capabilities**: Filter by names, values, dates, numerical ranges, text patterns\n- **Advantages**: Declarative, no coding required, consistent performance\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear, static criteria for selection\n\n**Script-Based Filtering (`type: \"script\"`)**\n- **Best For**: Complex logic, dynamic criteria, or business rules\n- **Capabilities**: Full programmatic control, access to complete metadata\n- **Advantages**: Maximum flexibility, can implement any filtering logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Simple rules aren't sufficient or logic needs to be dynamic\n","properties":{"type":{"type":"string","description":"Determines which filtering mechanism to use. This choice affects which properties\nmust be configured and how filtering logic is implemented.\n\n**Available types**\n\n**Rule-Based Filtering (`\"expression\"`)**\n- **Required Config**: The `expression` object with rule definitions\n- **Behavior**: Evaluates declarative rules against item attributes\n- **Best For**: Common patterns like name matching, date ranges, value limits\n- **Advantages**: Simpler to configure, no custom code required\n\n**Script-Based Filtering (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to determine which items to process\n- **Best For**: Complex conditions, business logic, dynamic criteria\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard filtering needs (name, size, date), use `\"expression\"`\n2. For complex logic or conditions not covered by expressions, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based filtering. This object enables filtering\nitems based on common attributes without requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define filtering rules that\ncan match against item attributes like name, type, value, date, and other properties.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Rules can be combined with AND/OR logic\n- Each rule can check a specific attribute\n- Multiple conditions can be applied (ranges, pattern matching, exact matches)\n\n**Common filter patterns**\n\n1. **Pattern matching**: Regular expressions with `matches`\n2. **Value range filtering**: Numbers between min and max values\n3. **Date range filtering**: Items created/modified within specific time ranges\n4. **Status checking**: Items with specific status values or properties\n\nFor AI agents: Rule-based filtering should be your first choice when the filtering criteria\ncan be expressed in terms of standard attributes. Only use script-based filtering when\nmore complex logic is required.\n","properties":{"version":{"type":"string","description":"Version identifier for the expression format. Currently only version \"1\" is supported.\n\nThis field ensures future compatibility if the expression format evolves. Always set to \"1\"\nfor current implementations.\n","enum":["1"]},"rules":{"type":"array","description":"Expression array defining filter conditions using prefix notation. The first element is the operator,\nfollowed by its operands which may themselves be nested expression arrays.\n\nThe rule expression follows this pattern:\n- First element: Operator name (string)\n- Remaining elements: Operands for that operator (values or nested expressions)\n\n**Expression structure**\n\nFilter expressions use a prefix notation where operators appear before their operands:\n```\n[operator, operand1, operand2, ...]\n```\n\n**Comparison Operators**\n- `\"equals\"`: Exact match (equals)\n- `\"notequals\"`: Not equal to value (not equals)\n- `\"greaterthan\"`: Value is greater than specified value (is greater than)\n- `\"greaterthanequals\"`: Value is greater than or equal to specified value (is greater than or equals)\n- `\"lessthan\"`: Value is less than specified value (is less than)\n- `\"lessthanequals\"`: Value is less than or equal to specified value (is less than or equals)\n- `\"startswith\"`: String starts with specified prefix (starts with)\n- `\"notstartswith\"`: String does not start with specified prefix (does not start with)\n- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"notendswith\"`: String does not end with specified suffix (does not end with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"empty\"`: Field is missing or empty (is empty)\n- `\"notempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches a regular expression, ignoring case (matches)\n\nOperator names are case-insensitive: `NOTEMPTY` and `endsWith` work and are stored\nas sent. `isempty`, `isnotempty`, `anyof`, `notcontains`, `doesnotstartwith`,\n`doesnotendwith`, `notmatches`, `doesnotmatch` and `notequal` are not operators:\nthey are refused with 422 \"Invalid operation: <name>\", with the name in lowercase,\nboth when the filter is saved and by `POST /v1/processors/filter`. To negate a\ncondition, wrap it in `not`.\n\nThe string comparisons (`equals`, `contains`, `startswith`, `endswith` and their\nnegations) are case-sensitive; wrap the field in `lowercase` to compare without case.\n`matches` is not: every pattern is compiled with the flags `gi`, so `^paid$` also\nmatches `PAID` and `[a-z]` matches capital letters. An inline flag such as `(?i)`\nmakes the pattern invalid. The filter still saves, but in a run every record fails\nwith `FILTER_EVALUATION_FAILED` (\"Invalid regular expression: /(?i)…/gi: Invalid\ngroup\"), and `POST /v1/processors/filter` refuses the call with 422 (live-verified\n2026-10-22).\n\n**Logical Operators**\n- `\"and\"`: All conditions must be true\n- `\"or\"`: At least one condition must be true\n- `\"not\"`: Negates the condition\n\n**Field Access and Type Conversion**\n- `\"extract\"`: Access a field from the item by name\n- `\"settings\"`: Access a custom setting from the flow, flow step, or integration configuration\n- `\"boolean\"`: Convert value to Boolean type\n- `\"epochtime\"`: Convert value to Epoch Time (Unix timestamp)\n- `\"number\"`: Convert value to Number type\n- `\"string\"`: Convert value to String type\n\n**Field Access Details**\n\n**Using `extract` to access record fields:**\n- Retrieves values from the current record being processed\n- Can access nested properties using dot notation (e.g., `\"customer.email\"`)\n- Paths are relative to the record itself: `[\"extract\", \"record.status\"]`\n  matches nothing\n- In a record filter, must be wrapped in a type conversion (`string`, `number`,\n  `boolean` or `epochtime`): a bare `[\"extract\", ...]` operand is refused with 422\n  \"Field operation extract must be called by a Type Conversion Operation but was called\n  by <operator>\", both when an import's filter is saved and by\n  `POST /v1/processors/filter`\n\n**Using `settings` to access configuration values:**\n- Retrieves values from the integration's configuration settings\n- Supports different scopes with prefix notation:\n  - `flow.settingName`: Access flow-level settings\n  - `flowGrouping.settingName`: Access the flow group's settings\n  - `export.settingName`: Access the export's (or lookup's) own settings\n  - `import.settingName`: Access the import's own settings\n  - `integration.settingName`: Access integration-level settings\n  - `connection.settingName`, `iClient.settingName`: Access the step's\n    connection and iClient settings\n- A scope whose resource has no settings can be absent; a rule that reads\n  it matches nothing\n- Must be wrapped in a type conversion like `extract`: a bare\n  `[\"settings\", ...]` operand is refused with 422 \"Field operation settings\n  must be called by a Type Conversion Operation\"\n- Useful for dynamic filtering based on configuration\n\n**Field Transformations**\n- `\"lowercase\"`: Convert string to lowercase\n- `\"uppercase\"`: Convert string to uppercase\n- `\"ceiling\"`: Round number up to the nearest integer\n- `\"floor\"`: Round number down to the nearest integer\n- `\"abs\"`: Get absolute value of a number\n\n**Arithmetic** (number operands)\n- `\"add\"`, `\"subtract\"`, `\"multiply\"`, `\"divide\"`, `\"modulo\"`: for example\n  `[\"subtract\", [\"number\", [\"extract\", \"total\"]], [\"number\", [\"extract\", \"discount\"]]]`.\n  `mod` is not an operator (422 \"Invalid operation: mod.\").\n\n**Dates**\n- `epochtime` reads ISO 8601 strings and epoch-millisecond numbers. An empty\n  string, a string that isn't a date, or epoch milliseconds sent as a string\n  fail it with \"Invalid date. Cannot convert given string or number to date\n  time.\", followed by `; record=` and the record as JSON: in a run that record fails (`FILTER_EVALUATION_FAILED`, source\n  `output_filter` on an export), and `POST /v1/processors/filter` and an\n  export preview refuse the whole call with 422. `and` and `or` evaluate\n  every operand, so a `notempty` test beside it doesn't prevent the failure.\n  A missing or null field doesn't match.\n- `datetime`, `date` and `timestamp` are not type conversions: 422\n  \"Invalid operation: <name>.\"\n- `number` reads a missing or empty field as 0.\n\n**Handlebars operands**\n- An operand can also be a string holding exactly one Handlebars expression, such as\n  `\"{{record.status}}\"` or `\"{{lowercase record.code}}\"`, on either side of the\n  operator. It renders against the record (`record.<field>`; an unprefixed\n  `{{status}}` renders empty) before the comparison.\n- The rendered value is a string: wrap it in `[\"number\", ...]` to compare it with a\n  number, or the rule is refused with 422.\n- `epochtime` does not render Handlebars: `[\"epochtime\", \"{{timestamp}}\"]` is refused\n  with 422 \"invalid datetime format\".\n- A string with text around the expression, or with more than one expression, is\n  compared as a literal.\n- `[\"number\", \"{{timestamp \\\"x\\\"}}\"]` is the current time in epoch milliseconds,\n  so `[\"greaterthanequals\", [\"epochtime\", [\"extract\", \"created\"]], [\"subtract\",\n  [\"number\", \"{{timestamp \\\"x\\\"}}\"], 604800000]]` keeps records created in the\n  last 7 days.\n- An unknown helper fails the rule with 422 \"Failed to evaluate handlebars\n  expression\".\n\nChoose the type conversion that matches the comparison — `number` for numeric\ncomparisons, `epochtime` for dates, `string` for text. For example:\n\n```json\n[\n  \"equals\",\n  [\n    \"number\",  // Convert to number before comparison\n    [\n      \"extract\",\n      \"quantity\"\n    ]\n  ],\n  100\n]\n```\n\nExample with datetime conversion:\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",  // Convert to Unix timestamp before comparison\n    [\n      \"extract\",\n      \"createdDate\"\n    ]\n  ],\n  1609459200000  // January 1, 2021 as Unix timestamp in milliseconds\n]\n```\n\nExample with transformations:\n```json\n[\n  \"and\",\n  [\n    \"matches\",\n    [\n      \"lowercase\",  // Convert to lowercase before matching\n      [\n        \"string\",\n        [\n          \"extract\",\n          \"categories\"\n        ]\n      ]\n    ],\n    \"netsuite\"\n  ],\n  [\n    \"notequals\",\n    [\n      \"string\",\n      [\n        \"extract\",\n        \"recurrence.pattern.type\"\n      ]\n    ],\n    \"\"\n  ]\n]\n```\n\nExample comparing a record field with a flow setting:\n```json\n[\n  \"equals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"trantype\"\n    ]\n  ],\n  [\n    \"string\",\n    [\n      \"settings\",\n      \"flow.trantype\"\n    ]\n  ]\n]\n```\n\n**Examples**\n\nExample 1: Status field is not equal to \"cancelled\"\n```json\n[\n  \"notequals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"status\"\n    ]\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"filename\"\n    ]\n  ],\n  \"HC\"\n]\n```\n\nExample 3: Amount is greater than 100\n```json\n[\n  \"greaterthan\",\n  [\n    \"number\",\n    [\n      \"extract\",\n      \"amount\"\n    ]\n  ],\n  100\n]\n```\n\nExample 4: Order date is after January 1, 2023 (UTC). Compare dates as `epochtime`\nagainst epoch milliseconds: a `string` comparison of ISO timestamps orders values with\ndifferent UTC offsets wrongly.\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",\n    [\n      \"extract\",\n      \"orderDate\"\n    ]\n  ],\n  1672531200000\n]\n```\n","items":{"oneOf":[{"title":"String","type":"string"},{"title":"Number","type":"number"},{"title":"Boolean","type":"boolean"},{"title":"Object","type":"object"},{"title":"Array","type":"array"},{"title":"Null","type":"null"}]}}}},"script":{"type":"object","description":"Configuration for programmable script-based filtering. This object enables complex, custom\nfiltering logic beyond what expression-based filtering can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to determine which items\nshould be processed.\n\n**Implementation approach**\n\nScript-based filtering works by:\n1. Executing the specified function from the referenced script\n2. Passing item data to the function\n3. Using the function's return value (true/false) to determine inclusion\n\n**Common use cases**\n\nScript filtering is ideal for:\n- Complex business logic that can't be expressed as simple rules\n- Dynamic filtering criteria that change based on external factors\n- Content-based filtering that requires deep inspection\n- Advanced pattern matching beyond simple wildcards\n- Multi-stage filtering with intermediate logic\n\nFor AI agents: Only use script-based filtering when expression-based filtering is insufficient.\nScript filtering requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to the Script resource that contains the filtering logic. This must be a valid\nObjectId of a Script resource in this account: any other id fails the save with 422\n(\"Only a script owned by the user or a script shared by the Integration App can be used\nat filter.script._scriptId.\").\n\nThe referenced script must contain the function specified in the `function` field\nand must be written to handle filtering specifically. The script receives\nitem data as its input and must return a boolean value indicating whether\nto process the item (true) or skip it (false).\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n"},"function":{"type":"string","description":"Name of the function within the script to execute for filtering decisions. This function\nmust exist in the script referenced by _scriptId.\n\n**Function requirements**\n\nThe specified function must:\n- Accept item data as its first parameter\n- Return a boolean value (true to process the item, false to skip it)\n- Handle errors gracefully\n- Execute efficiently (as it may run for many items)\n\n**Function signature**\n\n```javascript\nfunction filterItems(itemData) {\n  // itemData contains properties of the item being evaluated\n  // Custom logic here\n  return true; // or false to skip the item\n}\n```\n\nFor AI agents: Ensure the function name exactly matches a function defined in the\nreferenced script, as mismatches will cause the filter to fail.\n"}}}}},"Distributed":{"type":"object","description":"Authentication settings for distributed (real-time listener) exports.","properties":{"bearerToken":{"type":"string","description":"Bearer token that a system pushing data into this export sends as\n`Authorization: Bearer <token>`. Optional — most distributed listeners (NetSuite,\nSalesforce real-time) use no token. Write-only and stored encrypted; treat it as a\nsensitive credential.","format":"password"}}},"Cdc":{"type":"object","description":"Change-data-capture configuration for real-time listener exports (the \"Listen for real-time\ndata\" source step). Present only when `type` is `stream`. Set on `MongodbExport` (MongoDB change\nstreams) and `RDBMSExport` (SQL Server CDC and PostgreSQL logical replication); the listener\nstreams change events continuously instead of fetching records on a schedule.\n\nOn save the API normalizes the `properties` grid into the typed fields: a `snapshot.mode`\nentry populates `snapshotMode` and a `capture.mode` entry populates `captureMode`. Every\nstream export must resolve a snapshot mode from one of those two places.","properties":{"captureMode":{"type":"string","enum":["change_streams_update_full","change_streams_update_full_with_pre_image"],"default":"change_streams_update_full","description":"How MongoDB change-stream events are materialized into records. Use\n`change_streams_update_full_with_pre_image` when downstream steps need the document's prior\nstate. MongoDB listeners only — `RDBMSExport` (SQL Server CDC, PostgreSQL) does not set this."},"snapshotMode":{"type":"string","enum":["initial","no_data","when_needed","initial_only"],"default":"no_data","description":"Whether the listener seeds existing rows before streaming changes. Use `initial` to backfill\nexisting data on the first run; `no_data` to stream only new changes."},"slotName":{"type":"string","pattern":"^[a-z_][a-z0-9_]{0,62}$","description":"Name of the PostgreSQL logical replication slot the listener consumes. PostgreSQL listeners\nonly. When omitted, the platform manages the slot name. Must be a valid PostgreSQL\nidentifier (lowercase letters, numbers, or underscores, starting with a letter or\nunderscore)."},"publicationName":{"type":"string","pattern":"^[a-z_][a-z0-9_]{0,62}$","description":"Name of the PostgreSQL publication that defines which tables stream changes. PostgreSQL\nlisteners only. When set, the publication is the sole table filter and `rdbms.tables`\nstays unset. Must be a valid PostgreSQL identifier."},"cursor":{"type":"string","description":"Server-managed stream position marker (where the listener resumes reading change events).\nMaintained by the platform as events are consumed; the UI exposes a reset workflow for it\nunder Cursor Management."},"paths":{"type":"array","items":{"type":"string"},"description":"Dot-notation paths within each raw change event to retain in the emitted record. MongoDB\nlisteners default to `[\"payload\"]` (the whole change document); SQL Server and PostgreSQL\nlisteners commonly select `payload.after`, `payload.op`, and related fields to expose the\nchanged row and operation type."},"intervalTime":{"type":"integer","default":300,"description":"Polling interval, in seconds, at which the listener checks the source for new change events."},"properties":{"type":"array","description":"Connector-engine settings passed through to the underlying CDC connector as name/value\npairs. Mirrors the form's additional-properties grid: the required keys (`capture.mode`\nand `snapshot.mode` for MongoDB, `snapshot.mode` for SQL Server and PostgreSQL) are\nserialized first, followed by optional keys such as `field.exclude.list`,\n`column.include.list`, or `snapshot.collection.filter.overrides.<db>.<collection>`.\nOn save the API also lifts `snapshot.mode` / `capture.mode` entries into the typed\n`snapshotMode` / `captureMode` fields.","items":{"type":"object","required":["name","value"],"properties":{"name":{"type":"string","description":"Connector property key (e.g. `capture.mode`, `snapshot.mode`, `field.exclude.list`)."},"value":{"type":"string","description":"Connector property value."}}}}}},"Topic":{"type":"object","description":"Subscription settings for an Event Streams listener (`adaptorType: TopicExport`). The\nlistener consumes messages published to the topic and hands them to the flow in batches;\nthe platform delivers them internally, so the export carries no `_connectionId`. Present\nwhen `adaptorType` is `TopicExport`; the server fills `startPosition` and `maxWaitTimeMs`\nwith their defaults when they are omitted.","properties":{"_topicId":{"type":"string","format":"objectId","description":"Topic this listener subscribes to. Create or look it up with `GET /v1/topics`; the\ntopic must belong to the same account. Pointing an existing listener at a different\ntopic starts a new subscription, read from `startPosition`. A clone made in the same\nenvironment — of this export, or of a flow that contains it — keeps `_topicId`, so the\ncopy becomes another listener on the topic once its flow is enabled."},"startPosition":{"type":"string","enum":["latest","earliest"],"default":"latest","description":"Where the listener starts reading when it first subscribes to `_topicId`. Use `earliest`\nto backfill retained history, `latest` (the default) to process only new messages. Read\non create and whenever `_topicId` changes; on other updates the stored value is kept even\nif a different value is sent."},"maxWaitTimeMs":{"type":"integer","minimum":1000,"default":30000,"description":"Longest time, in milliseconds, the listener waits to fill a batch before delivering\nwhatever it has collected. Lower values reduce latency for sparse topics; the default\nof 30 seconds favors fuller batches. The batch record cap is the export's `pageSize`."}}},"FileSystem":{"type":"object","description":"Defines which files to read from a local or mounted folder on the host running the\non-premise agent. Required when the _connectionId field references a file-system (on-premise)\nconnection; must not be included for other connection types.","required":["directoryPath"],"properties":{"directoryPath":{"type":"string","description":"Folder on the on-premise agent's host to read files from; the agent's OS account must\nhave read permission on it. Accepts a local OS path or a UNC network share, and supports\nhandlebars templates for dynamic folders."}}},"Http":{"type":"object","description":"Configuration for HTTP exports. Required when the export's `_connectionId` references a\nconnection of type `http`, and on `NetSuiteHTTPExport` (NetSuite's REST APIs on a `netsuite`\nconnection, where `nsDomainType` supplies the host).","properties":{"type":{"type":"string","enum":["file","blob","csvstream"],"description":"Set to `file` when the export works with files rather than a record-returning endpoint: raw file downloads (PDFs, images, binary data) and the folder mode of the cloud file providers (Google Drive, Box, Dropbox, Azure Blob, Google Cloud Storage, Celigo Storage), where each file in the folder is parsed by the `file` object's `type` (csv, json, xml, xlsx, filedefinition — including EDI) into records; the `file` object must also be configured. Leave undefined for standard data exports that return structured records (JSON, XML, GraphQL, SOAP responses) — the response is then parsed into records for downstream steps. When set to `file` or `blob`, the export appears as a \"Transfer\" step in Flow Builder instead of a standard \"Export\" step."},"formType":{"type":"string","enum":["http","rest","graph_ql","assistant","assistant_graphql"],"description":"Authoring mode of the HTTP request form. Controls which editor the UI presents\n(standard HTTP, legacy REST, GraphQL, or connector-assistant driven) and how the\nrequest fields are interpreted. When omitted, the standard HTTP form is used."},"method":{"type":"string","description":"HTTP method used to request data from the target API. Consult the target API's\ndocumentation to determine the appropriate method. `HEAD` exists for connector endpoints\nthat return metadata in response headers only (Azure Data Lake Storage Gen2 path\nproperties).","enum":["GET","POST","PUT","PATCH","DELETE","HEAD"]},"followRedirects":{"type":"boolean","default":true,"description":"When explicitly false, 3xx responses are not followed — the redirect\nresponse itself (status code, `Location` header, body) becomes the\nrecord. Omitted or true follows allowed redirects (the default\nbehavior)."},"maxRedirects":{"type":"integer","minimum":1,"maximum":10,"description":"Caps how many consecutive 3xx redirects are followed. Only applies\nwhen `followRedirects` is not false; to not follow at all, set\n`followRedirects: false` rather than `maxRedirects: 0`. Decimal\nvalues are rejected on save."},"relativeURI":{"type":"string","description":"Resource path appended to the connection's `baseURI` to form the complete request URL. Path segments, query parameters, or the entire value can be built with handlebars expressions for endpoints determined at runtime. For lookup exports (`isLookup: true`) with mappings configured, handlebars always render against the original pre-mapped input record, so fields removed or renamed by mappings remain available for URI construction. On `NetSuiteHTTPExport` the base URL is derived from the NetSuite connection and `nsDomainType`, so the path starts at `/services/rest/...` or `/app/site/hosting/restlet.nl`.\n\nLeave it empty to call the connection's `baseURI` exactly; `/` appends a trailing slash, which is a different URL. An export saved with an empty relative URI and no `http.body` comes back from reads without its `http` block, so a PUT built from such a read loses the stored settings: send the complete `http` block with every PUT."},"headers":{"type":"array","description":"Headers specific to this export, merged with (and able to override) headers defined on the connection. Define common headers such as authentication on the connection instead. Values support handlebars expressions; for lookup exports with mappings, values render against the pre-mapped input record.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header to send; a header defined here overrides a same-named header from the connection."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions; for lookup exports with mappings, the value renders against the pre-mapped input record."}}}},"requestMediaType":{"type":"string","description":"Overrides the connection-level request media type for this export. Set only when this endpoint requires a different format than the connection default.","enum":["json","xml","urlencoded","form-data","plaintext"]},"body":{"type":"string","description":"Request body sent with POST, PUT, or PATCH requests, typically carrying query or filter criteria for APIs (such as GraphQL or SOAP) that expect them in the body. The content must match the format set by `requestMediaType` and supports handlebars expressions for dynamic values."},"successMediaType":{"type":"string","description":"Media type used to parse successful response bodies. Set only when the response format differs from the request format.","enum":["json","xml","csv","plaintext"]},"errorMediaType":{"type":"string","description":"Media type used to parse error response bodies. Set only when error responses use a different format than the request.","enum":["json","xml","plaintext"]},"_asyncHelperId":{"type":"string","format":"objectId","description":"AsyncHelper resource that handles polling for long-running operations on APIs that process requests asynchronously (HTTP 202 responses, job tickets, feed or document IDs). Set when the export must submit a request, poll for status, and retrieve results once the external process completes — for example Amazon SP-API feeds or large report generators."},"once":{"type":"object","description":"Callback configuration for once exports, used to mark records as exported in the source system after successful processing.\nCalled only while the export's `type` is `once`: an export with no `type` keeps a saved `once` and never calls it, so every\nrun reads every record again (live-verified 2026-10-22).","properties":{"relativeURI":{"type":"string","description":"Relative path (starting with `/`) called on the source system to mark each record as exported after successful processing. Supports handlebars variables and renders against the pre-mapped record — mappings do not apply to the callback URI."},"method":{"type":"string","description":"HTTP method used for the mark-as-exported callback request.","enum":["GET","PUT","POST","PATCH","DELETE"]},"body":{"type":"string","description":"Request body sent with the mark-as-exported callback. Supports handlebars expressions for dynamic values."}}},"paging":{"type":"object","description":"Controls how the export requests subsequent pages when the API returns multi-page responses. The `method` field determines which companion fields are required and how each next page is requested. For the page, skip, and token methods, reference the matching pagination variable (`{{export.http.paging.page}}`, `{{export.http.paging.skip}}`, or `{{export.http.paging.token}}`) in the relative URI or request body — misconfigured pagination is a common cause of incomplete data retrieval.\n\nWhen this export is the first lookup of a builder-mode API with `pagination.enabled` set, it is that API's volume driver: saving the export (even unchanged) invalidates the API's issued cursors, so a caller mid-walk gets an empty final page, and removing `paging` is accepted on save but makes every call to the API fail with `400 api_pagination_source_not_paged` until paging is restored or the API's flag is turned off.\n\nWhen a next page's response is identical to the previous one, paging stops: the export records an open error with code `page_response_error` (\"Identical response received from next page request. No more page requests will be made.\") and the run completes with the pages already read, so later pages are never exported. A request that doesn't change between pages produces this, as observed with `method: page` and the pagination variable missing from the relative URI and body; reference the variable so each request asks for the next page.","properties":{"method":{"type":"string","description":"Pagination strategy used to request each subsequent page; match it to the mechanism documented by the target API. Determines which companion fields are required: `token` needs `path` and `pathLocation`, `url` needs `path`, `relativeuri` needs `relativeURI`, `body` needs `body`, while `linkheader` typically needs no extra configuration. Using the wrong method results in errors or incomplete data retrieval.","enum":["linkheader","page","skip","token","url","relativeuri","body"]},"page":{"type":"integer","description":"Starting page number for `method: page`. Set to 1 for APIs whose first page is not zero-indexed; when omitted, paging starts at 0. The value is incremented automatically for each subsequent page request."},"skip":{"type":"integer","description":"Starting offset for `method: skip`. Rarely needed — most APIs start at 0, and the value is incremented by the page size automatically for each subsequent request. Set only when the API requires a non-zero starting offset."},"token":{"type":"string","description":"Initial token for `method: token`. Leave empty for normal pagination — the first request is sent without a token, and subsequent tokens are extracted from each response via `path`. Set only to resume from a known token or for APIs that require a token on the first request."},"path":{"type":"string","description":"Location of the pagination value in each response — the continuation token for `method: token`, or the complete next-page URL for `method: url`. When `pathLocation` is `body`, use a dot-notation JSON path (e.g. `meta.nextToken`); when `header`, use the exact case-sensitive header name. Not used by other pagination methods."},"pathLocation":{"type":"string","description":"Where the export looks for the pagination token referenced by `path`. Required for `method: token`; not used by other pagination methods.","enum":["body","header"]},"pathAfterFirstRequest":{"type":"string","description":"Alternative token location used for responses after the first page, in the same format as `path`. Set only when the API moves the token to a different location in subsequent responses — setting it unnecessarily can cause pagination to fail."},"relativeURI":{"type":"string","description":"Overrides the main relative URI for second and later page requests; leave empty when the main relative URI works for all pages. Build it with handlebars using the `previous_page` context — `previous_page.full_response` (the prior response body), `previous_page.last_record` (the last record of the prior page), and `previous_page.headers` (the prior response headers)."},"body":{"type":"string","description":"Overrides the main request body for second and later page requests, typically for GraphQL or SOAP APIs that paginate through the body; leave empty when the main body works for all pages. Build it with handlebars using the `previous_page` context — `previous_page.full_response`, `previous_page.last_record`, and `previous_page.headers`."},"mergeBodyParamsToPagingBody":{"type":"boolean","description":"Only applies when `paging.method` is `body`. When true, the body\nparameters produced by the export's mappings at runtime are merged\ninto the evaluated `paging.body` for page 2+ requests, so paging\nrequests keep the same mapped parameters as the first page.\nOmitted/false keeps the original behavior — page 2+ requests send\nonly the evaluated `paging.body`."},"linkHeaderRelation":{"type":"string","description":"Link header relation followed for `method: linkheader` when the API uses a value other than the default `next`. Case-sensitive and must exactly match the `rel` value in the Link header, without the `rel=` prefix."},"resourcePath":{"type":"string","description":"Overrides the path to records for second and later page responses. Set only when follow-up pages place records at a different location than the first response; leave empty when all pages share the same structure."},"lastPageStatusCode":{"type":"integer","description":"HTTP status code that signals the last page, replacing the default behavior of treating 404 as the end of pagination. When this status is received (any code but `200` paired with `lastPagePath`), paging stops and the response is not treated as an error. Set it when the API uses a non-404 code (such as 204 or 400) to indicate no more pages, or to `200` together with `lastPagePath` when the last page is an ordinary 200 response: then the response is parsed, its records are kept, and paging stops only when `lastPagePath` matches `lastPageValues`."},"lastPagePath":{"type":"string","description":"JSON path to a response-body field that signals the end of pagination, such as a \"has more\" flag or a cursor that empties on the last page. Must be used with `lastPageValues`, which lists the values at this path that stop paging. If the path does not exist in a response, the condition is not considered met. With `method: token` the path is read only on a response whose status code equals `lastPageStatusCode`, so pair it with `lastPageStatusCode: 200` for an ordinary last page; with `method: body` it is read on every response."},"lastPageValues":{"type":"array","description":"Values at `lastPagePath` that stop pagination; a match on any entry ends paging. All entries are compared as exact, case-sensitive strings, even for boolean or numeric fields — use `\"true\"`, `\"false\"`, `\"null\"` for JSON null, or `\"\"` for an empty string. Required when `lastPagePath` is set.","items":{"type":"string"}},"maxPagePath":{"type":"string","description":"JSON path to the total page count in the response, used to stop paging once the last page is reached. Only applies to the `page` and `skip` methods. Point it at the total number of pages, not the current page number."},"maxCountPath":{"type":"string","description":"JSON path to the total record count in the response, used to stop paging once all records have been retrieved. Only applies to the `page` and `skip` methods; when both are set, `maxPagePath` takes precedence. Point it at the total number of records, not the count in the current page."}},"if":{"properties":{"method":{"const":"body"}},"required":["method"]},"then":{"required":["body"]},"else":{"if":{"properties":{"method":{"const":"token"}},"required":["method"]},"then":{"required":["path","pathLocation"]},"else":{"if":{"properties":{"method":{"const":"relativeuri"}},"required":["method"]},"then":{"required":["relativeURI"]},"else":{"if":{"properties":{"method":{"const":"url"}},"required":["method"]},"then":{"required":["path"]}}}}},"response":{"type":"object","description":"Controls how records are extracted from the API response and how success or failure is detected at the response level. When the API wraps records in an envelope object, set `resourcePath` to the path of the records array — without it, the entire response body is treated as a single record. Leave this object undefined when the API returns a bare JSON array.","properties":{"resourcePath":{"type":"string","description":"Dot-separated path to the array of records inside the response body (e.g. `data.customers` for `{\"data\": {\"customers\": [...]}}`); without it, a wrapped response is treated as a single record. Values starting with `$` are evaluated as JSONPath instead of dot notation — including unions (`$['orders','invoices'][*]`) and filter expressions (`$.items[?(@.qty>3)]`); all other values keep the existing dot-notation behavior unchanged. Leave undefined when the API returns a bare JSON array. This extracts records from the API response — not to be confused with `oneToMany`/`pathToMany`, which unwrap arrays from input records, or `paging.resourcePath`, which applies only to subsequent page responses."},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"},"resourceIdPath":{"type":"string","description":"Path to the unique identifier within each record, used primarily when processing results of asynchronous import responses. When omitted, the system looks for standard `id` or `_id` fields automatically."},"successPath":{"type":"string","description":"Path to a response field that signals whether the call succeeded, for APIs that return HTTP 200 even on failure. With `successValues`, the value at this path must match one of them; without `successValues`, the path only has to resolve — a response in which it is absent fails with `response_failure` (\"Unable to extract paths from response body using path: <path>, Response: …\")."},"successValues":{"type":"array","items":{"type":"string"},"description":"Values at `successPath` that mark the response as successful; any other value is treated as an error. All comparisons are string-based — use `\"true\"` or `\"false\"` for boolean fields."},"errorPath":{"type":"string","description":"Path to the error message in the response body. The value at this path is included in error logs and error records when the API returns an error; without it, the message is the whole response body."},"failPath":{"type":"string","description":"Path to a response field that signals failure even when the HTTP status code is 200 — the inverse of `successPath`. With `failValues`, the response fails when the value there matches one of them; without, when the path holds a truthy value. A path that resolves to an array matches only when the array has exactly one element."},"failValues":{"type":"array","items":{"type":"string"},"description":"Values at `failPath` that mark the response as failed, even when the HTTP status code is 200."},"allowArrayforSuccessPath":{"type":"boolean","description":"When true, treats the value at `successPath` as an array and counts the response as successful if any element matches `successValues`, rather than requiring a single scalar match. Set it for APIs that return per-record status arrays in a batch response."},"twoDArray":{"type":"object","description":"Parsing options for endpoints that return tabular data as a two-dimensional array (rows of cells) rather than an array of objects, such as spreadsheet-style or report APIs.","properties":{"hasHeader":{"type":"boolean","description":"When true, the first row is treated as column headers and used to name the fields of each generated record."},"doNotNormalize":{"type":"boolean","description":"When true, the rows are passed through as raw arrays instead of being normalized into keyed records."}}},"blobFormat":{"type":"string","description":"Controls how the binary response body is decoded for blob exports. Only relevant when `http.type` is `file` or the export type is `blob`.","enum":["utf8","ucs2","utf-16le","ascii","binary","base64","hex"]}}},"_httpConnectorVersionId":{"type":"string","format":"objectId","description":"Pins the export to a version of the published HTTP connector its connection is bound to\n(`connection.http._httpConnectorId`), so the UI renders that connector's endpoint form instead\nof a hand-authored request. Linked on save together with `_httpConnectorResourceId` and\n`_httpConnectorEndpointId` — see that field for the rule."},"_httpConnectorResourceId":{"type":"string","format":"objectId","description":"The connector resource (a group of related endpoints, such as Orders) that this export's\nendpoint belongs to. Linked on save together with `_httpConnectorEndpointId` — see that field\nfor the rule."},"sendAuthForFileDownloads":{"type":"boolean","description":"When true, includes authentication headers when downloading files."},"_httpConnectorEndpointId":{"type":"string","format":"objectId","description":"The connector endpoint this export calls, shown in the UI as the selected endpoint. On save the\nplatform derives it (with the version and resource ids) from `relativeURI` and `method` when\nthey match an endpoint of the connector the connection is bound to — path parameters, literal\nids and query strings still match — and replaces ids that belong to another connector. An id for\nthe connection's own connector is kept as sent even when the URI names a different endpoint;\nwhen nothing matches, or the connection has no connector, whatever was sent is kept and nothing\nis derived."}}},"Salesforce":{"type":"object","description":"Configuration for Salesforce exports. Required when the export's `_connectionId` references a Salesforce connection; omit for other connection types. The `type` field selects the extraction mode: `soql` runs batch queries and requires the `soql` object, while `distributed` listens for real-time events and requires the `distributed` object. File retrieval (when the export's `type` is `blob`) requires `sObjectType` and `id` instead.","properties":{"type":{"type":"string","description":"Selects the extraction mode and determines which configuration object is required. Use `soql` for scheduled or on-demand batch queries and lookups — it requires the `soql` object and works with both the `rest` and `bulk` APIs and the standard, delta, test, and once export types. Use `distributed` for real-time event handling — it requires the `distributed` object, always appears as a Listener in Flow Builder, ignores the `api` field, and supports only the standard export type.","enum":["soql","distributed"]},"sObjectType":{"type":"string","description":"API name of the Salesforce object the export operates on; the object must exist in the connected org and be accessible to the integration user. Required for distributed exports and for blob exports — blob exports accept only the file storage objects (`Attachment`, `ContentVersion`, `Document`) paired with the `id` field. Optional for SOQL exports, where the object can be inferred from the query."},"id":{"type":"string","description":"Salesforce record ID of the file to retrieve; required for blob exports and not used for `soql` or `distributed` exports. Accepts a static ID or a handlebars expression such as `{{record.Attachment_Id__c}}` to resolve the file at runtime — dynamic IDs require the export to run as a lookup (`isLookup: true`). The ID must belong to the object named in `sObjectType`."},"includeDeletedRecords":{"type":"boolean","description":"When true, SOQL exports use Salesforce's `queryAll()` instead of `query()`, including Recycle Bin records deleted within the past 15 days; each record's `IsDeleted` field identifies deletions. Useful for synchronizing deletes to target systems or maintaining a complete audit trail. Ignored for distributed and blob exports.","default":false},"api":{"type":"string","description":"Salesforce API used to run SOQL queries; ignored for distributed and blob exports. Use `rest` (the default when omitted) for smaller datasets (under 10,000 records) and for lookup exports — the Bulk API is not compatible with `isLookup: true`. Use `bulk` for large data volumes and higher throughput, optionally tuned through the `bulk` object.","enum":["rest","bulk"]},"bulk":{"type":"object","description":"Settings for Salesforce Bulk API 2.0 jobs. Only applies when `api` is `bulk` on a SOQL export; ignored otherwise.","properties":{"maxRecords":{"type":"integer","description":"Caps how many records a single Bulk API job retrieves, which helps prevent timeouts with complex queries or large records. When omitted, Salesforce's default applies. Lower values suit complex or custom objects; higher values improve throughput for simple records.","minimum":10000},"purgeJobAfterExport":{"type":"boolean","default":true,"description":"When true, deletes the Bulk API job in Salesforce after all data is retrieved, keeping the Bulk Data Load Jobs list clean — at the cost of making job details unavailable for later troubleshooting. Has no effect on the data retrieved or the success of the export."}}},"soql":{"type":"object","description":"SOQL query configuration; required when `type` is `soql` and not used otherwise. The query controls which objects, fields, and filter conditions the export retrieves, with either the REST or Bulk API.\n\nRequired when type is soql.","properties":{"query":{"type":"string","description":"SOQL statement passed directly to Salesforce that defines which objects, fields, and records the export retrieves. Supports handlebars — for delta exports, filter with `WHERE LastModifiedDate > {{lastExportDateTime}}` — and relationship queries that fetch parent and child records together. Select only the fields you need and use `ORDER BY` for consistent results across pages.","maxLength":200000}}},"distributed":{"type":"object","description":"Real-time listener configuration — the object that makes an export a listener. Set it, with `type: distributed`, when creating one; a listener saved from the integrator.io form carries it with no `type`, and the platform fires it either way. Not used on SOQL or file exports. Records reach the flow only when an Apex trigger on `sObjectType` in the connected org calls the Celigo package (`integrator_da__.RealTimeExporter.processExport()`) — no scheduling or manual execution is involved. Integration Apps create their triggers at installation; for a custom flow the customer deploys the trigger in Salesforce, starting from the code the export page shows under Required trigger.","properties":{"referencedFields":{"type":["array","null"],"description":"Fields from related objects to include in the exported record, written in dot notation (e.g. `Account.Name`, `Owner.Email`, `Custom_Lookup__r.Field__c`). Works with lookup and master-detail relationships, up to 10 unique referenced relationships per export. Referenced fields are retrieved via separate API calls, so include only fields the integration actually needs.","items":{"type":["string","null"]}},"disabled":{"type":"boolean","description":"When true, the listener stops processing events while its configuration and Salesforce triggers stay in place. Events that occur while disabled are ignored, not queued — they are not processed retroactively when re-enabled, so consider a delta export to catch up after extended pauses."},"qualifier":{"type":["string","null"],"description":"SOQL WHERE-clause condition that filters which record events the listener processes; when omitted (stored as `null`), every event for the object is processed. The Celigo Salesforce package evaluates it inside Salesforce as `SELECT Id FROM <sObjectType> WHERE (<qualifier>) AND Id IN :ids` before anything is sent, so only fields of the monitored object and SOQL comparison syntax apply — Salesforce formula functions are not part of this grammar. A deleted record is never returned by that query, so a listener with any qualifier receives no delete events; leave it unset when removals must trigger the flow."},"batchSize":{"type":["integer","null"],"description":"Controls how many event records are grouped into each real-time processing batch. Stored as `null` when not configured. Smaller batches lower latency for time-sensitive operations; larger batches improve throughput for high-volume objects. Does not limit how many records are processed in total — only how they are grouped.","minimum":4,"maximum":200},"skipExportFieldId":{"type":["string","null"],"description":"API name of a Salesforce checkbox field used to prevent infinite loops in bidirectional syncs: when the integration updates a record, this field is set so the resulting event is ignored, then cleared automatically. Required for bidirectional sync scenarios. The field must be a checkbox dedicated to integration use and updateable by the integration user."},"relatedLists":{"type":["array","null"],"description":"Child record sets to include with the parent record when it changes, one entry per related list. When omitted (stored as `null`), only the primary record is processed. Each related list adds Salesforce API calls and increases payload size.","items":{"type":"object","description":"Defines how to retrieve one type of child record related to the primary object. Configure multiple entries to retrieve different types of children.","properties":{"referencedFields":{"type":"array","description":"API names of the child object fields to retrieve; only the listed fields are included, and an empty array retrieves only the `Id` field. Include only fields the integration needs — each field adds data volume and processing time.","items":{"type":["string","null"]}},"parentField":{"type":"string","description":"API name of the lookup or master-detail field on the child object that references the parent (e.g. `AccountId`, `Parent_Object__c`) — the field's API name, not the relationship name. Used to build the query that fetches children for each parent record."},"sObjectType":{"type":"string","description":"Case-sensitive API name of the child object to retrieve (e.g. `Contact`, `Custom_Child__c`) — use the API name, not the label. The object must have a relationship field to the parent and be accessible to the connected user."},"filter":{"type":"string","description":"SOQL condition that limits which child records are included; when omitted, all related children are retrieved. Provide only the condition expression without the `WHERE` keyword — it is combined automatically with the parent relationship filter."},"orderBy":{"type":"string","description":"Sort order for the retrieved child records, as field names with optional `ASC`/`DESC` directions (e.g. `CreatedDate DESC`). Provide only the fields and directions without the `ORDER BY` keywords. When omitted, Salesforce determines the order."}},"required":["parentField","referencedFields","sObjectType"]}}}}},"if":{"properties":{"type":{"const":"distributed"}},"required":["type"]},"then":{"required":["distributed","sObjectType"]},"else":{"if":{"required":["distributed"]},"then":{},"else":{"if":{"required":["id"]},"then":{"required":["sObjectType"]},"else":{"if":{"not":{"propertyNames":{"enum":["metadata"]}}},"then":{"required":["soql","type"]}}}}},"AS2":{"type":"object","description":"Configures AS2 exports and listeners. Optional — the transport settings live on the AS2\nconnection, and an AS2 export is valid with only `file` (parse settings); set this object\nto link a Trading Partner Connector or to transfer raw files as blobs. An AS2 listener\nacts as the flow's source, receiving trading-partner transmissions in near real-time and\nhandling decryption, signature verification, and MDN generation; AS2 (Applicability\nStatement 2) transmits EDI and other data securely over HTTP/S using S/MIME encryption\nand digital signatures.","properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies the partner-specific EDI configuration —\ncommunication protocol, document schemas, mappings, validation rules, and endpoint\ndetails. Set this to link the export to all settings required for AS2 communication\nwith that partner."},"blob":{"type":"boolean","description":"When true, retrieves raw files without parsing them into structured records (rendered as\na \"Transfer\" step in the flow UI). Use only when the file contents are not needed in\nsubsequent steps — for binary files or when parsing is handled downstream. Only\navailable on AS2 and VAN exports."}},"required":[]},"DynamoDB":{"type":"object","description":"Defines how records are queried from DynamoDB tables. Required when the _connectionId field\nreferences a DynamoDB connection; must not be included for other connection types. Basic\nexports need region, method, tableName, keyConditionExpression, expressionAttributeNames,\nand expressionAttributeValues; once exports (export type \"once\") additionally need\nonceExportPartitionKey, plus onceExportSortKey for composite-key tables.","required":["region","tableName","keyConditionExpression","expressionAttributeNames","expressionAttributeValues"],"properties":{"region":{"type":"string","enum":["us-east-1","us-east-2","us-west-1","us-west-2","af-south-1","ap-east-1","ap-south-1","ap-northeast-1","ap-northeast-2","ap-northeast-3","ap-southeast-1","ap-southeast-2","ca-central-1","eu-central-1","eu-west-1","eu-west-2","eu-west-3","eu-south-1","eu-north-1","me-south-1","sa-east-1"],"description":"AWS region hosting the DynamoDB table. Must match the region where the table is\ndeployed so the integration can reach it.","default":"us-east-1"},"method":{"type":"string","enum":["query"],"description":"DynamoDB operation used to retrieve items. Only \"query\" is currently supported."},"tableName":{"type":"string","description":"Name of the DynamoDB table to query. Must exactly match an existing table (case-sensitive)."},"keyConditionExpression":{"type":"string","description":"Key condition determining which items the query retrieves. Must include a condition on\nthe partition key and may add sort-key conditions (equality, BETWEEN, begins_with).\nReference attribute names with \"#\" placeholders defined in expressionAttributeNames and\nvalues with \":\" placeholders defined in expressionAttributeValues."},"filterExpression":{"type":"string","description":"Filters query results on non-key attributes after the key condition is applied; omit to\nreturn all items matching the key condition. Uses the same \"#\" and \":\" placeholders\ndefined in expressionAttributeNames and expressionAttributeValues."},"projectionExpression":{"type":"array","items":{"type":"string"},"description":"Attributes to return from each item, reducing data transfer; omit to return all\nattributes. Each array element is one field, referenced via \"#\" placeholders defined in\nexpressionAttributeNames."},"expressionAttributeNames":{"type":"string","description":"JSON string mapping \"#\" placeholders to actual attribute names, e.g.\n{\"#pk\": \"customerId\"}. Placeholders defined here are used in keyConditionExpression,\nfilterExpression, and projectionExpression."},"expressionAttributeValues":{"type":"string","description":"JSON string mapping \":\" placeholders to comparison values, e.g. {\":status\": \"ACTIVE\"}.\nValues can be static or dynamic handlebars expressions such as {{lastExportDateTime}},\nand are referenced from keyConditionExpression and filterExpression."},"onceExportPartitionKey":{"type":"string","description":"Partition key attribute that uniquely identifies items when the export's type is \"once\".\nCeligo uses it to mark items as processed after a successful export, preventing the same\nitems from being exported again on subsequent runs."},"onceExportSortKey":{"type":"string","description":"Sort key attribute used together with onceExportPartitionKey to identify processed items\nwhen the table has a composite primary key. Omit for tables keyed by a partition key\nalone."},"pathToRecords":{"$ref":"#/components/schemas/PathToRecords"},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"}}},"PathToRecords":{"type":"string","maxLength":250,"description":"Path to the array of records inside each document or item fetched from the source; each array element is emitted as an individual record, which keeps records within the 5 MB page-size limit even when the source document exceeds it. Accepts dot notation (e.g. `data.orders`) or JSONPath for values starting with `$`, including unions (`$['orders','invoices'][*]`) and filter expressions (`$.items[?(@.qty>3)]`); a path that resolves to nothing emits zero records for that document or item. When omitted, each document or item is emitted as one record, exactly as before — HTTP and file-based exports configure this same capability through their existing `http.response.resourcePath` and `file.json`/`file.xml` `resourcePath` fields."},"FTP":{"type":"object","description":"Defines which files to retrieve from an FTP, FTPS, or SFTP server. Required when the\n_connectionId field references an FTP/SFTP connection; must not be included for other\nconnection types. directoryPath selects the folder, fileNameStartsWith/fileNameEndsWith\nfilter files by name, and backupDirectoryPath controls where files are moved after\nretrieval.","required":["directoryPath"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies partner-specific B2B settings for this export.\nWhen set, the export inherits the connector's pre-configured settings; omit to use only\nthe FTP connection details."},"directoryPath":{"type":"string","description":"Directory on the server to retrieve files from, either absolute or relative to the login\ndirectory; the FTP user must have read permission on it. Use forward slashes regardless\nof server OS — paths are case-sensitive on UNIX/Linux servers. Supports handlebars\ntemplates, e.g. archive/{{date 'YYYY-MM-DD'}}."},"fileNameStartsWith":{"type":"string","description":"Only retrieves files whose names start with this value (case-sensitive on most servers);\naccepts static text or handlebars templates. When combined with fileNameEndsWith, files\nmust match both."},"fileNameEndsWith":{"type":"string","description":"Only retrieves files whose names end with this value, commonly a file extension\n(case-sensitive on most servers). When combined with fileNameStartsWith, files must\nmatch both."},"backupDirectoryPath":{"type":"string","description":"Directory on the same server where files are moved after successful export, giving you\nan independent backup; if omitted, files are simply deleted from the source directory\nafter successful export unless `file.skipDelete` is true (Celigo also keeps its own copy\nof processed files for a set period). Supports static paths or handlebars templates."}}},"JDBC":{"type":"object","description":"Configuration object for JDBC (Java Database Connectivity) data integration exports.\n\nThis object is REQUIRED when the _connectionId field references a JDBC database connection\nand must not be included for other connection types. It defines how data is extracted\nfrom relational databases using SQL queries.\n\n**Jdbc export capabilities**\n- Execute custom SQL SELECT statements\n- Support for joins, aggregations, and functions\n- Flexible data retrieval from any accessible tables or views\n- Compatible with all major database systems\n**Critical:** WHAT BELONGS IN THIS OBJECT\n- `query` - **ALWAYS REQUIRED** - The SQL SELECT statement\n- `once` - **REQUIRED** when the export's Object Type is `\"once\"` (set _include_once: true)\n- **DO NOT** put `delta` inside this object - delta is handled via the query\n\n**Delta exports (type: \"delta\")**\nFor delta/incremental exports, do NOT populate a `delta` object inside `jdbc`.\nInstead, use `{{lastExportDateTime}}` or `{{currentExportDateTime}}` directly in the query:\n```json\n{\n  \"type\": \"delta\",\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE updatedAt > {{lastExportDateTime}}\"\n  }\n}\n```\n\n**Once exports (type: \"once\")**\nFor once exports (mark records as processed), populate `jdbc.once.query`:\n```json\n{\n  \"type\": \"once\",\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE exported = false\",\n    \"once\": {\n      \"query\": \"UPDATE customers SET exported = true WHERE id = {{record.id}}\"\n    }\n  }\n}\n```\n\n**Standard exports (type: null or not specified)**\nJust provide the query:\n```json\n{\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE status = 'ACTIVE'\"\n  }\n}\n```","required":["query"],"properties":{"formType":{"type":["string","null"],"enum":["sql","simple",null],"description":"Selects how the export's query is authored. Use `sql` for a hand-written SQL statement in\n`query`; use `simple` to build the query from the `simple` object's table/columns/filter."},"query":{"type":"string","description":"SQL SELECT statement executed to retrieve data, from simple table selections to joins and\naggregations. For delta exports, include {{lastExportDateTime}}/{{currentExportDateTime}}\nin the WHERE clause rather than configuring a separate delta object."},"once":{"type":"object","description":"**CRITICAL: REQUIRED when the export's Object Type is \"once\".**\n\nIf Object Type is \"once\", you MUST set _include_once to true (or include this object).\n\nThis object has ONLY ONE property: \"query\" (a SQL UPDATE string).\nDO NOT create any other properties like \"update\", \"table\", \"set\", \"where\", etc.\n\nCORRECT format:\n```json\n{\"query\": \"UPDATE customers SET exported=true WHERE id={{record.id}}\"}\n```\n\nWRONG format (DO NOT DO THIS):\n```json\n{\"update\": {\"table\": \"customers\", \"set\": {...}}}\n```\n","properties":{"query":{"type":"string","description":"**REQUIRED** - A SQL UPDATE statement string to mark records as processed.\n\nThis is a plain SQL UPDATE query string, NOT a structured object.\n\nThe query runs AFTER each record is successfully exported, setting a flag\nto indicate the record has been processed.\n\nFormat: \"UPDATE <table> SET <column>=<value> WHERE <id_column>={{record.<id_field>}}\"\n\nExample: \"UPDATE customers SET exported=true WHERE id={{record.id}}\"\n\nThe {{record.id}} placeholder is replaced with the actual record ID from each exported row.\n"}}},"simple":{"type":"object","description":"Visual query-builder configuration used when `formType` is `simple`; the platform builds\nthe SELECT from these instead of raw SQL. Only relevant when `formType` is `simple`.","properties":{"table":{"type":"string","description":"Table or view to select rows from."},"columns":{"type":"array","items":{"type":"string"},"description":"Columns to return; an empty list selects all columns."},"filter":{"description":"Filter applied as the WHERE clause for the generated query, stored as the\nplatform's structured filter-rules object.","allOf":[{"$ref":"#/components/schemas/Filter"}]}}}}},"MongoDB":{"type":"object","description":"Defines how documents are retrieved from MongoDB collections. Required, with `method` and\n`collection`, when the _connectionId field references a MongoDB connection and the export\nfetches documents; must not be included for other connection types. A change-stream\nlistener (`type: stream`) is configured through `cdc` instead and may omit this object, or\nset only `collection` to scope the listener. Supports find queries with optional filter\ncriteria and field projections.","properties":{"method":{"type":"string","enum":["find"],"description":"MongoDB operation used to retrieve documents. Only \"find\" is currently supported,\nequivalent to db.collection.find(filter, projection)."},"collection":{"type":"string","description":"Name of the MongoDB collection to query. Case-sensitive and must reference an existing\ncollection in the connected database. Required on find exports; on a change-stream\nlistener (`type: stream`) it is optional and may list several collections separated by\ncommas, matching the listener form's \"Collections\" field."},"filter":{"type":"string","description":"MongoDB query document, as a JSON string, that selects which documents to export; omit\nto return every document in the collection. Supports standard MongoDB query operators\nand handlebars variables for dynamic values — for example\n{\"lastModified\": {\"$gt\": \"{{lastRun}}\"}} for incremental processing."},"projection":{"type":"string","description":"MongoDB projection document, as a JSON string, that limits which fields are returned;\nomit to return all fields. Use 1 to include fields or 0 to exclude them — the two modes\ncannot be mixed except for _id, which is included by default unless explicitly excluded.\nProjection affects only the shape of returned documents, not which documents match."},"pipeline":{"type":"string","description":"MongoDB aggregation pipeline, as a JSON-array string of stages, used instead of a plain\nfind when documents need to be transformed, joined, or grouped server-side (e.g. `$match`,\n`$lookup`, `$group`). When set, it takes the place of `filter`/`projection`. Supports\nhandlebars variables for incremental processing."},"readPreference":{"type":"string","enum":["doNotOverride","primary","primaryPreferred","secondary","secondaryPreferred","nearest"],"default":"doNotOverride","description":"Overrides which replica-set member this export reads from, trading consistency for load\ndistribution. Leave at `doNotOverride` to inherit the connection's setting."},"pathToRecords":{"$ref":"#/components/schemas/PathToRecords"},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"}}},"NetSuite":{"type":"object","description":"NetSuite-specific export configuration. Required when `_connectionId` references a NetSuite\nconnection; omit for all other connection types. Supports saved-search, RESTlet, and\ndistributed (SuiteApp) exports, plus two file cabinet modes: blob exports transfer files\nas-is (set the export's top-level `type` to `blob` and configure `netsuite.blob`), while\nfile exports parse file contents into records (leave the export's top-level `type` unset\nand configure `netsuite.file`).","properties":{"type":{"type":"string","enum":["search","basicSearch","metadata","selectoption","restlet","getList","getServerTime","distributed","file"],"description":"Controls how data is retrieved from NetSuite and which sibling object must be configured:\n`search` pairs with `netsuite.searches`, `restlet` with `netsuite.restlet`, `distributed`\nwith `netsuite.distributed`, and `file` with `netsuite.file.folderInternalId`. For blob\nexports (raw file transfer without parsing), leave this field unset, set the export's\ntop-level `type` to `blob`, and configure `netsuite.internalId` instead. For lookups\n(`isLookup: true`), prefer `restlet`, which supports `suiteapp2.0` saved searches with\ndynamic inputs; `search` is limited for dynamic lookups."},"searches":{"type":"array","description":"Saved search configurations that query NetSuite for records. Each entry references a\nsaved search by internal ID, names the record type being searched, and can add filter\ncriteria.","items":{"type":"object","properties":{"savedSearchId":{"type":"string","description":"Internal ID of the NetSuite saved search to run."},"recordType":{"type":"string","description":"NetSuite record type being searched, as its exact lowercase script ID\n(e.g. \"customer\", \"salesorder\") — not the display name."},"criteria":{"type":"array","description":"Additional filter criteria applied on top of the saved search.","items":{"type":"object","properties":{"field":{"type":"string","description":"Script-id of the search column to filter on.  REQUIRED.\nMust come from the record type's SEARCH COLUMNS list\n(NOT the body-fields list).  Becomes the `name`\nargument of NetSuite's `search.createFilter()`."},"operator":{"type":"string","description":"Comparison operator.  REQUIRED.  Supported values\ndepend on the column's data type (e.g. `is`,\n`isnot`, `after`, `before`, `anyof`,\n`noneof`, `contains`, `startswith`,\n`greaterthan`, `lessthan`).  Must be a string\nNetSuite recognises -- mismatched operators cause\nruntime rejection."},"join":{"type":"string","description":"Optional join name when filtering through a related\nrecord (e.g. `customer` from a sales order).  Use\nonly when the column lives on a related record and\nNetSuite supports the join from the base record type."},"searchValue":{"description":"Value (or array of values for `anyof`-style\noperators) to compare against the column.  Required\nfor most operators; omit for the few unary operators\n(e.g. `isempty`, `isnotempty`)."}},"required":["field","operator"]}}}}},"metadata":{"type":"object","description":"Supplemental metadata associated with the NetSuite export."},"selectoption":{"type":"object","description":"Configuration for `selectoption` exports, which retrieve the available select options\nfor a NetSuite field."},"customFieldMetadata":{"type":"object","description":"Metadata describing the custom fields defined in the NetSuite account."},"skipGrouping":{"type":"boolean","description":"When true, each saved-search result row becomes its own record. When false or omitted,\nconsecutive result rows that share the record's internal id are grouped into one record\nshaped as an array of rows — the *Group rows* option in the UI."},"statsOnly":{"type":"boolean","description":"When true, returns only summary statistics about matching records instead of the\ndetailed records themselves."},"internalId":{"type":"string","description":"Internal ID of the file in the NetSuite file cabinet to export. Required for blob\nexports (the export's top-level `type` is `blob`); the file is transferred as-is\nwithout parsing. For parsed file exports, use `netsuite.file.folderInternalId` instead."},"blob":{"type":"object","properties":{"purgeFileAfterExport":{"type":"boolean","description":"When true, permanently deletes the file from the NetSuite file cabinet after a\nsuccessful export. When false or omitted, the file is left in place."}},"description":"Configuration for blob exports, which transfer files from the NetSuite file cabinet\nas-is without parsing them into records. To export a blob, set the export's top-level\n`type` to `blob`, set `netsuite.internalId` to the file's internal ID, and leave\n`netsuite.type` unset. Use `netsuite.file` instead when file contents should be parsed\ninto records."},"restlet":{"type":"object","properties":{"recordType":{"type":"string","description":"NetSuite record type the RESTlet operates on, as its script ID. Custom record\ntypes use the \"customrecord_\" prefix."},"batchSize":{"type":"number","description":"Number of records the RESTlet returns per request (default 1000). This, not the\nexport's `pageSize`, sizes each NetSuite call; the export keeps requesting until the\nsearch is exhausted. Larger batches reduce round-trips but raise NetSuite governance\nand memory use per call; tune to the record's size. The RESTlet caps a call at 1000,\nso a larger value acts as 1000. In a test run or preview the request is capped at the\nexport's `pageSize` (default 20) instead."},"searchId":{"type":["string","null"],"description":"Internal ID of the NetSuite saved search the RESTlet executes. Results follow the\nsaved search's current definition, so editing the search in NetSuite changes the\nexport output without changing this ID."},"useSS2Restlets":{"type":"boolean","description":"When true, calls SuiteScript 2.0 RESTlets. When false or omitted, legacy\nSuiteScript 1.0 RESTlets are used.\nSet at step creation together with restletVersion — the same\ncreation-time-only constraint applies (see restletVersion)."},"restletVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"RESTlet version the export invokes. Defaults to `suiteapp2.0` when\n`useSS2Restlets` is true, `suitebundle` otherwise.\nThe version is fixed when the step is created — the Advanced selector is\ndisabled on existing steps, and migrating an existing export to a\ndifferent version means recreating or cloning it."},"criteria":{"type":"array","description":"Filter conditions added on top of the saved search (the UI's \"Additional search\ncriteria\"). Each condition pairs a search column with an operator and one or two\ncomparison values, and the conditions are combined with AND — every one must match.","items":{"type":"object","properties":{"field":{"type":"string","description":"Script-id of the search column to filter on. This is the primary\nfield the criterion is evaluated against (e.g. \"trandate\", \"status\")."},"join":{"type":"string","description":"Join relationship that applies this filter to a related record's fields instead\nof the base record. Must match a join name NetSuite defines for the record type."},"operator":{"type":"string","enum":["after","allof","any","anyof","before","between","contains","doesnotcontain","doesnotstartwith","equalto","greaterthan","greaterthanorequalto","haskeywords","is","isempty","isnot","isnotempty","lessthan","lessthanorequalto","noneof","notafter","notallof","notbefore","notbetween","notequalto","notgreaterthan","notlessthan","notlessthanorequalto","noton","notonorafter","notonorbefore","notwithin","on","onorafter","onorbefore","startswith","within"],"description":"Comparison operator applied between the column and the search value, using\nNetSuite's lowercase search operator names. Which operators a column accepts\ndepends on its data type."},"searchValue":{"description":"Value the column is compared against — a string, a number, or an array of\nvalues for `anyof`-style operators. On lookup steps it may be a Handlebars\nexpression rendered from the incoming record (for example `{{poNumber}}`)."},"searchValue2":{"description":"Second comparison value for the two-value operators `between` and `notbetween`\n(the upper bound). Omit for every other operator."},"formula":{"type":"string","description":"NetSuite formula expression used as the filter condition, for criteria that\nstandard field-operator-value comparisons can't express."}}}},"columns":{"type":"array","description":"Search columns the RESTlet returns for each matching record. With a saved search,\nthe RESTlet returns the search's own result columns followed by any listed here: a\nlist adds columns and never removes one of the search's, so remove a column in the\nsaved search itself. An ad-hoc search (no `searchId`) must list its columns here.","items":{"type":"object","properties":{"_id":{"type":"string","readOnly":true,"description":"Subdocument id the server assigns to the column entry on save. Not a NetSuite\nfield id — never set it when authoring."},"name":{"type":"string","description":"Column field name as referenced in the search."},"join":{"type":"string","description":"Join relationship that sources this column from a related record instead of\nthe base record. Supports dot notation for nested joins (e.g. \"employee.manager\")."},"summary":{"type":"string","enum":["group","sum","count","avg","min","max"],"description":"Summary type applied to this column, turning the search into a summary search."},"formula":{"type":"string","description":"NetSuite formula expression that computes this column's value at runtime, such\nas a CASE statement or date formatting, instead of reading a stored field."},"label":{"type":"string","description":"Display name for the column; it becomes the property name of the column's value\nin each exported record."},"sort":{"type":"boolean","description":"When true, query results are sorted by this column's values."}}}},"markExportedBatchSize":{"type":"integer","description":"Number of records updated per RESTlet call when marking records as exported in\nNetSuite (live: an integer count, e.g. 100).","maximum":100,"minimum":1},"hooks":{"type":["object","null"],"properties":{"batchSize":{"type":"number","description":"Legacy location of the RESTlet page size; `restlet.batchSize` is the current\nfield and takes precedence when both are set."},"preSend":{"type":"object","properties":{"fileInternalId":{"type":"string","description":"Internal ID of the JavaScript file in the NetSuite File Cabinet that defines\nthe hook function. Must be readable by the role behind the export's connection."},"function":{"type":"string","description":"Name of the global function in that file that NetSuite invokes as the pre\nsend hook."},"configuration":{"type":"object","description":"Free-form JSON stored with the hook reference for the script's own use. Custom\nsettings in scope for the export are passed to the function separately as\n`options.settings`."}},"description":"SuiteScript hook that runs inside NetSuite after a page of records is collected\nand before it is sent to the Celigo platform."}},"description":"SuiteScript hooks for the export. Only `preSend` exists on exports; imports carry\nthe pre map, post map, and post submit hooks."},"cLocked":{"type":"object","description":"Lock state that prevents modification of the configuration when set."}},"description":"Configuration for `restlet` exports. Identifies the RESTlet script that retrieves data\nfrom NetSuite, along with the saved search, criteria, and columns it executes with."},"distributed":{"type":"object","properties":{"recordType":{"type":"string","description":"NetSuite record type the listener is deployed on, as its lowercase script ID\n(`customer`, `salesorder`, `customrecord_<scriptid>`) — never the display name or a\ncustom record's numeric internal ID. Types the platform's record-type catalog marks\n`doesNotSupportRTExport` cannot host a listener and are not offered in the UI."},"executionContext":{"type":"array","description":"NetSuite execution contexts that trigger this distributed export. A record change\nfires the export only when it occurs in one of the listed contexts.","default":["userinterface","webstore"],"items":{"type":"string","enum":["userinterface","webservices","csvimport","offlineclient","portlet","scheduled","suitelet","custommassupdate","workflow","webstore","userevent","mapreduce","restlet","webapplication","restwebservices"]}},"disabled":{"type":"boolean","description":"When true, the listener's NetSuite deployment is inactive and record saves no longer\ntrigger the export. Maintained by the platform as the flow is disabled and re-enabled;\nre-enabling the flow re-registers the deployment. A listener is created disabled, and\na flow created already enabled leaves it that way until a flow update sets\n`disabled: false`, which activates it within seconds."},"executionType":{"type":"array","description":"Record operations that trigger this distributed export. A record event fires the\nexport only when its operation matches one of the listed types.","default":["create","edit","xedit"],"items":{"type":"string","enum":["create","edit","delete","xedit","copy","view","cancel","approve","reject","pack","ship","markcomplete","reassign","editforecast","dropship","specialorder","orderitems","paybills","print","email"]}},"qualifier":{"type":["array","string","null"],"description":"Field-specific qualification criteria the SuiteApp evaluates against the record before\nexporting it; saves that do not match are discarded. Stored as a filter-expression array\nof `[field, operator, value]` rules joined by lowercase `\"and\"` / `\"or\"`, as a legacy\nexpression string, or null when no criteria are set."},"skipExportFieldId":{"type":"string","description":"Script ID of a checkbox body field on the record type. A save with that box checked is\nskipped by the listener, which then clears the box itself — used to keep a mass update\nfrom fanning out and to stop a write-back import from re-triggering the listener."},"hooks":{"type":"object","description":"SuiteScript hook that runs inside NetSuite before the record leaves it.","properties":{"preSend":{"type":"object","description":"Pre-send hook: a SuiteScript function in the NetSuite file cabinet invoked before\nrecords leave NetSuite, with the full SuiteScript API available — for example to run\ndynamic searches against the record being exported.","properties":{"fileInternalId":{"type":"string","description":"Internal ID of the file cabinet file that holds the hook's SuiteScript."},"function":{"type":"string","description":"Name of the function in that file to invoke for each export."},"configuration":{"type":"object","description":"Settings passed to the hook function."}}}}},"sublists":{"type":["array","null"],"description":"IDs of the NetSuite sublists to include with each exported record (for example `item`\nfor transaction line items). Records are exported with body fields only when no\nsublists are selected. Legacy documents may store null (equivalent to no sublists).","items":{"type":"string"}},"referencedFields":{"type":"object","description":"Field references recorded on the listener by the platform. Not exposed in the listener\nform; leave as stored."},"relatedLists":{"type":"object","description":"Related record lists recorded on the listener by the platform. Not exposed in the\nlistener form; leave as stored."},"forceReload":{"type":"boolean","description":"When true, the listener reloads the record from the database after the save before\nexporting it, so fields that are populated only once the save completes — such as\ntransaction line IDs on new lines — are included. The reload slows every save of that\nrecord type."},"ioEnvironment":{"type":"string","description":"integrator.io environment the listener's NetSuite deployment reports to. Written by the\nplatform when the listener registers with NetSuite; live-observed values include\n`production` and `staging`."},"ioDomain":{"type":"string","description":"integrator.io domain the listener's NetSuite deployment calls back to — the account's\nown regional domain, stamped by the platform on every save and echoed on create and\nupdate responses; not returned by GET."},"lastSyncedDate":{"type":"string","format":"date-time","description":"When the listener's deployment last synchronized with NetSuite. Written by the platform;\nnot set until the first successful synchronization."},"settings":{"type":"object","description":"Additional key-value settings for the distributed export. Not exposed in the listener\nform."},"useSS2Framework":{"type":"boolean","description":"Derived by the platform from `frameworkVersion` — true when it is `suiteapp2.0`, absent\notherwise — and echoed on create and update responses; not returned by GET. A value\nsent on create or update is replaced by the derived one."},"frameworkVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"SuiteApp framework version used by the distributed export.\nFixed when the step is created — the Advanced selector is disabled on\nexisting steps, and migrating an existing listener to a different\nversion means recreating or cloning it."}},"description":"Configuration for `distributed` exports, which use the NetSuite SuiteApp to fire\nreal-time, event-driven exports when records change. Define the record type to listen\nto and the execution contexts and operations that trigger it."},"getList":{"type":"array","description":"Configuration for `getList` exports — a list of record references, each retrieving one\nNetSuite record by its identifier. The export returns the fetched records in list order.","items":{"type":"object","properties":{"type":{"type":"string","description":"Standard NetSuite record type to retrieve (e.g. \"customer\", \"salesOrder\").\nUsed when `typeId` is absent. Case-sensitive."},"typeId":{"type":"string","description":"Script ID of a custom record type (\"customrecord_...\") or custom transaction\ntype (\"customtransaction_...\"). Takes precedence over `type` when present.\nCase-sensitive."},"internalId":{"type":"string","description":"Internal ID of the NetSuite record to retrieve. Assigned by NetSuite at record\ncreation and unique within the record type. Takes precedence over `externalId`."},"externalId":{"type":"string","description":"Identifier assigned by an external system, used to reference and synchronize the\nrecord across systems. Distinct from the NetSuite-assigned internal ID."}}}},"searchPreferences":{"type":"object","properties":{"bodyFieldsOnly":{"type":"boolean","description":"When true, search results include only the record's body fields and exclude fields\nfrom joined or related records. Reduces payload size when related data isn't needed."},"pageSize":{"type":"number","description":"Number of search results NetSuite returns per page."},"returnSearchColumns":{"type":"boolean","description":"When true, search results include the column data defined in the search. When\nfalse, column data is omitted and results contain only minimal record information."}},"description":"Preferences that control how NetSuite executes searches and shapes the results it returns."},"file":{"type":"object","description":"Configuration for file exports, which retrieve files from the NetSuite file cabinet\nand parse their contents (CSV, XML, JSON) into records. Leave the export's top-level\n`type` unset when using this object; for raw transfers without parsing, use\n`netsuite.blob` with the export's `type` set to `blob` instead.","properties":{"folderInternalId":{"type":"string","description":"Internal ID of the NetSuite file cabinet folder to export files from. Accepts a\nhandlebars expression (e.g. `{{record.folderId}}`) when the folder must be\nselected dynamically from record data. The ID is stable across folder renames and\nmoves, but may differ between non-production and production accounts."},"backupFolderInternalId":{"type":"string","description":"Internal ID of the NetSuite file cabinet folder where backup files are stored.\nIDs are account-specific and do not transfer between non-production and production\nenvironments."},"fileNameStartsWith":{"type":"string","description":"Optional prefix filter. Only files whose names start with this string are\nexported from the configured folder."},"fileNameEndsWith":{"type":"string","description":"Optional suffix filter. Only files whose names end with this string (e.g. a\n\".csv\" extension) are exported from the configured folder."}}}},"required":[]},"RDBMS":{"type":"object","description":"Configuration object for Relational Database Management System (RDBMS) data integration exports.\n\nThis object defines how data is read from a relational database and must not be included\nfor other connection types. For query-type exports (standard, delta, once) it is REQUIRED\nwhen the _connectionId field references an RDBMS database connection and holds the SQL\nquery. For real-time stream listeners (`type: \"stream\"`) it holds the CDC watch list\n(`tables`) instead — and a PostgreSQL listener scoped by a publication\n(`cdc.publicationName`) may omit this object entirely.\n\n**Rdbms export capabilities**\n- Execute custom SQL SELECT statements\n- Support for joins, aggregations, and functions\n- Flexible data retrieval from any accessible tables or views\n- Compatible with all major database systems\n\n**Critical:** WHAT BELONGS IN THIS OBJECT\n- `query` - The SQL SELECT statement - required for every query-type export (standard, delta, once)\n- `once` - **REQUIRED** when the export's Object Type is `\"once\"` (set _include_once: true)\n- `tables` - the CDC listener watch list - real-time stream listeners (`type: \"stream\"`) only\n- **DO NOT** put `delta` inside this object - delta is handled via the query\n- **DO NOT** author `query` on a stream listener - real-time CDC exports have no SQL query\n\n**Delta exports (type: \"delta\")**\nFor delta/incremental exports, do NOT populate a `delta` object inside `rdbms`.\nInstead, use `{{lastExportDateTime}}` or `{{currentExportDateTime}}` directly in the query, and\nset the export's top-level `delta.startDate`: without it the first run's window is empty.\n```json\n{\n  \"type\": \"delta\",\n  \"delta\": { \"startDate\": \"2026-01-01T00:00:00.000Z\" },\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE updatedAt > {{lastExportDateTime}}\"\n  }\n}\n```\n\n**Once exports (type: \"once\")**\nFor once exports (mark records as processed), populate `rdbms.once.query`:\n```json\n{\n  \"type\": \"once\",\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE exported = false\",\n    \"once\": {\n      \"query\": \"UPDATE customers SET exported = true WHERE id = {{record.id}}\"\n    }\n  }\n}\n```\n\n**Standard exports (type: null or not specified)**\nJust provide the query:\n```json\n{\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE status = 'ACTIVE'\"\n  }\n}\n```\n\n**Real-time CDC listener exports (type: \"stream\")**\nSQL Server and PostgreSQL connections support change-data-capture listeners that stream row\nchanges continuously (the `cdc` object holds the listener settings). A stream export has NO\n`query` — it watches `tables`:\n```json\n{\n  \"type\": \"stream\",\n  \"rdbms\": { \"tables\": \"public.orders,public.customers\" },\n  \"cdc\": { \"properties\": [{ \"name\": \"snapshot.mode\", \"value\": \"no_data\" }] }\n}\n```\nA PostgreSQL listener can instead scope tables through a publication\n(`cdc.publicationName`), in which case `tables` stays unset and the `rdbms` object may be\nomitted entirely.","properties":{"tables":{"type":"string","description":"Comma-separated list of tables the CDC listener watches for changes. Used only by real-time\nlistener exports (`type` is `stream`); standard SQL exports use `query` instead. SQL Server\nlisteners use fully-qualified three-part names (`database.schema.table`); PostgreSQL\nlisteners use two-part names (`schema.table`)."},"query":{"type":"string","description":"SQL SELECT statement executed to retrieve data, from simple table selections to joins,\naggregations, and parameterized queries with Handlebars expressions. Query-type exports\nonly — real-time stream listeners (`type` is `stream`) have no SQL query. For delta exports,\nreference {{lastExportDateTime}}/{{currentExportDateTime}} directly in the query rather\nthan configuring a separate delta object.\n\nDouble braces render a string as a quoted SQL literal with its apostrophes escaped, and\ntriple braces insert a value raw. Observed in Snowflake queries:\n\n- String helper output (`dateFormat`, `jsonSerialize`) and both delta variables are\n  quoted the same way; a number or boolean renders bare, whether it is a JSON value or a\n  numeric helper's result (`{{add 2 3}}` renders `5`).\n- In a run, {{lastExportDateTime}} and {{currentExportDateTime}} arrive as quoted ISO 8601\n  UTC literals with milliseconds (`'2026-01-15T00:00:00.000Z'`), or in `delta.dateFormat`\n  when it is set: add no quotes of your own, and don't parse them with a format mask\n  written for another format.\n- A preview of an unsaved export document renders both as empty, unquoted text, so a\n  delta query fails to compile there unless the document carries both in `postData`.\n- The quoting leaves backslashes, which Snowflake reads as escape sequences, so a\n  double-brace value holding a backslash arrives changed. Pass such a value in a\n  dollar-quoted constant with triple braces (`$${{{record.field}}}$$`), which Snowflake\n  reads as is, but only when it can't contain `$$`: a `$$` ends the constant, and the rest\n  is read as SQL."},"once":{"type":"object","description":"**CRITICAL: REQUIRED when the export's Object Type is \"once\".**\n\nIf Object Type is \"once\", you MUST set _include_once to true (or include this object).\n\nThis object has ONLY ONE property: \"query\" (a SQL UPDATE string).\nDO NOT create any other properties like \"update\", \"table\", \"set\", \"where\", etc.\n\nCORRECT format:\n```json\n{\"query\": \"UPDATE customers SET exported=true WHERE id={{record.id}}\"}\n```\n\nWRONG format (DO NOT DO THIS):\n```json\n{\"update\": {\"table\": \"customers\", \"set\": {...}}}\n```\n","properties":{"query":{"type":"string","description":"**REQUIRED** - A SQL UPDATE statement string to mark records as processed.\n\nThis is a plain SQL UPDATE query string, NOT a structured object.\n\nThe query runs AFTER each record is successfully exported, setting a flag\nto indicate the record has been processed.\n\nFormat: \"UPDATE <table> SET <column>=<value> WHERE <id_column>={{record.<id_field>}}\"\n\nExample: \"UPDATE customers SET exported=true WHERE id={{record.id}}\"\n\nThe {{record.id}} placeholder is replaced with the actual record ID from each exported row.\n"}}}}},"S3":{"type":"object","description":"Defines which files to retrieve from an Amazon S3 bucket. Required when the _connectionId\nfield references an AWS S3 connection; must not be included for other connection types.\nregion and bucket locate the source, keyStartsWith/keyEndsWith filter objects by key, and\nbackupBucket with keyPrefix controls where files are moved after retrieval.","required":["region","bucket"],"properties":{"region":{"type":"string","default":"us-east-1","description":"AWS region where the bucket is located. Case-insensitive; the value is normalized to\nlowercase."},"bucket":{"type":"string","description":"S3 bucket to retrieve files from. The connection's AWS credentials must have\ns3:ListBucket and s3:GetObject permissions on it."},"keyStartsWith":{"type":"string","description":"Only retrieves objects whose keys start with this value (case-sensitive), effectively\nselecting a folder in S3's flat key structure. When combined with keyEndsWith, objects\nmust match both."},"keyEndsWith":{"type":"string","description":"Only retrieves objects whose keys end with this value (case-sensitive), commonly a file\nextension. When combined with keyStartsWith, objects must match both."},"backupBucket":{"type":"string","description":"Bucket in the same region where files are moved after successful export, giving you an\nindependent backup; if omitted, files are simply deleted from the source bucket after\nsuccessful export unless `file.skipDelete` is true (Celigo also keeps its own copy of\nprocessed files for a set period). The connection's AWS credentials must have\ns3:PutObject permission on this bucket."},"keyPrefix":{"type":"string","description":"Prefix prepended to each file's name when it is moved to the backup bucket; accepts\nstatic text or handlebars templates. The original directory structure is not preserved —\nonly the filename is appended to this prefix. Ignored unless backupBucket is set."}}},"Wrapper":{"type":"object","description":"Configuration for Wrapper exports, which delegate data retrieval to custom connector code\n(typically a stack-hosted function) rather than a built-in adaptor. Required when the\n_connectionId field references a wrapper connection.","required":["function"],"properties":{"function":{"type":"string","description":"Name of the function the wrapper invokes to retrieve records. Must match a callable\nfunction in the wrapper's execution context; names are case-sensitive."},"configuration":{"oneOf":[{"type":"object","additionalProperties":true},{"type":"array","maxItems":0}],"description":"Free-form settings passed to the wrapper function at runtime (connector-specific keys such\nas method, apiVersion, headers, relativePath, or body). Structure is defined by the wrapper\ncode, not by this schema. May be stored as an empty array when no settings exist."}}},"Parsers":{"type":"array","description":"Configuration for parsing XML payloads (i.e. files, HTTP responses, etc.). Use this field when you need to process XML data\nand transform it into a JSON records.\n\n**Implementation notes**\n\n- This is where you configure how to parse XML data in your resource\n- Although defined as an array, you typically only need a single parser configuration\n- Currently only XML parsing is supported\n- Only configure this field when working with XML data that needs structured parsing\n","items":{"type":"object","properties":{"version":{"type":"string","description":"Version identifier for the parser configuration format. Currently only version \"1\" is supported.\n\nAlways set this field to \"1\" as it's the only supported version at this time.\n","enum":["1"]},"type":{"type":"string","description":"Defines the type of parser to use. Currently only \"xml\" is supported.\n\nWhile the system is designed to potentially support multiple parser types in the future,\nat this time only XML parsing is implemented, so this field must be set to \"xml\".\n","enum":["xml"]},"name":{"type":"string","description":"Optional identifier for the parser configuration. This field is primarily for documentation\npurposes and is not functionally used by the system.\n\nThis field can be omitted in most cases as it's not required for parser functionality.\n"},"rules":{"type":"object","description":"Configuration rules that determine how XML data is parsed and converted to JSON.\nThese settings control the structure and format of the resulting JSON records.\n\n**Parsing options**\n\nThere are two main parsing strategies available:\n- **Automatic parsing**: Simple but produces more complex output\n- **Custom parsing**: More control over the resulting JSON structure\n","properties":{"V0_json":{"type":"boolean","description":"Controls the XML parsing strategy.\n\n- When set to **true** (Automatic): XML data is automatically converted to JSON without\n  additional configuration. This is simpler to set up but typically produces more complex\n  and deeply nested JSON that may be harder to work with.\n\n- When set to **false** (Custom): Gives you more control over how the XML is converted to JSON.\n  This requires additional configuration (like listNodes) but produces cleaner, more\n  predictable JSON output.\n\nMost implementations use the Custom approach (false) for better control over the output format.\n"},"listNodes":{"type":"array","description":"Paths of elements that always parse as arrays. An element that can repeat parses as an\nobject when it occurs once and as an array when it occurs more than once, so list every\nrepeating element here to give each record the same shape regardless of item count.\nEach entry is the element's full path from the record element selected by the resource\npath (from the document root when there is no resource path), with an optional leading\nslash. Only relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"includeNodes":{"type":"array","description":"Limits which XML nodes are included in the output JSON.\n\nFor large XML documents, you can use this field to extract only the nodes you need,\nreducing the size and complexity of the resulting JSON. Only nodes specified here\n(and their children) will be included in the output.\n\nEach entry should be a simplified XPath expression pointing to nodes to include.\n\nOnly relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"excludeNodes":{"type":"array","description":"Specifies which XML nodes should be excluded from the output JSON.\n\nSometimes it's easier to specify which nodes to exclude rather than which to include.\nUse this field to identify nodes that should be omitted from the output JSON.\n\nEach entry should be a simplified XPath expression pointing to nodes to exclude.\n\nOnly relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"stripNewLineChars":{"type":"boolean","description":"Controls whether newline characters are removed from text values.\n\nWhen set to true, all newline characters (\\n, \\r, etc.) will be removed from\ntext content in the XML before conversion to JSON.\n","default":false},"trimSpaces":{"type":"boolean","description":"Controls whether leading and trailing whitespace is trimmed from text values.\n\nWhen set to true, all values will have leading and trailing whitespace removed\nbefore conversion to JSON.\n","default":false},"attributePrefix":{"type":"string","description":"Specifies a character sequence to prepend to XML attribute names when converted to JSON properties.\n\nIn XML, both elements and attributes can exist at the same level, but in JSON this distinction is lost.\nTo maintain the distinction between element data and attribute data in the resulting JSON, this prefix\nis added to attribute names during conversion.\n\nFor example, with attributePrefix set to \"Att-\" and an XML element like:\n```xml\n<product id=\"123\">Laptop</product>\n```\n\nThe resulting JSON would be:\n```json\n{\n  \"product\": \"Laptop\",\n  \"Att-id\": \"123\"\n}\n```\n\nThis helps maintain the distinction between element content and attribute values in the\nconverted JSON, making it easier to reference specific data in downstream processing steps.\n"},"textNodeName":{"type":"string","description":"Specifies the property name to use for element text content when an element has both\ntext content and child elements or attributes.\n\nWhen an XML element contains both text content and other nested elements or attributes,\nthis field determines what property name will hold the text content in the resulting JSON.\n\nFor example, with textNodeName set to \"value\" and an XML element like:\n```xml\n<item id=\"123\">\n  Laptop\n  <category>Electronics</category>\n</item>\n```\n\nThe resulting JSON would be:\n```json\n{\n  \"item\": {\n    \"value\": \"Laptop\",\n    \"category\": \"Electronics\",\n    \"id\": \"123\"\n  }\n}\n```\n\nThis allows for unambiguous parsing of complex XML structures that mix text content with\nchild elements. Choose a name that's unlikely to conflict with actual element names in your XML.\n"}}}}}},"Mappings":{"type":"array","description":"Array of field mapping configurations for transforming data from one format into another.\n\n**Guidance**\n\nThis schema is designed around RECURSION as its core architectural principle. Understanding this recursive\nnature is essential for building effective mappings:\n\n1. The schema is self-referential by design - a mapping can contain nested mappings of the same structure\n2. Complex data structures (nested objects and arrays of objects, to any depth) are ALL\n   handled through this recursive pattern; an array of arrays is not (build one in a script hook)\n3. Each mapping handles one level of the data structure; deeper levels are handled by nested mappings\n\nWhen generating mappings programmatically:\n- For simple fields (string, number, boolean): Create single mapping objects\n- For objects: Create a parent mapping with nested 'mappings' array containing child field mappings\n- For arrays: Use 'buildArrayHelper' with extract paths defining array inputs and\n  recursive 'mappings' to define object structures\n\nThe system will process these nested structures recursively during runtime, ensuring proper construction\nof complex hierarchical data while maintaining excellent performance.\n\nOn save, a leaf row with neither a source (`extract`, `hardCodedValue`, `lookupName`) nor a\ndestination (`generate`) is dropped silently, at every nesting level, and so is an object or\nobject-array row left with no source, no destination and no remaining child `mappings` or\n`buildArrayHelper` entries; a row with only one of the two, and a source-less, destination-less\nwrapper that still has children, are kept.\n\nThe shape of the array is the mapper's **JSON structure**. Named top-level items produce an object\nrecord (`record { }`). Exactly one top-level item with `dataType: objectarray` and no `generate` is\nthe `rows [ ]` structure: the mapped record is an array — usually one element per element of the\nsource array that root's `buildArrayHelper` iterates — Handlebars templates such as `http.body`\nbind it as `rows` instead of `record`, and the mapping editor shows that row's destination disabled.\n","items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}}},"items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}},"Transform":{"type":"object","description":"Configuration for transforming data during processing operations. This object enables\nreshaping of records.\n\n**Transformation capabilities**\n\nCeligo's transformation engine offers powerful features for data manipulation:\n- Precise field mapping with JSONPath expressions\n- Support for any level of nested arrays\n- Formula-based field value generation\n- Dynamic references to flow and integration settings\n\n**Implementation approaches**\n\nThere are two distinct transformation mechanisms available:\n\n**Rule-Based Transformation (`type: \"expression\"`)**\n- **Best For**: Most transformation scenarios from simple to complex\n- **Capabilities**: Field mapping, formula calculations, lookups, nested data handling\n- **Advantages**: Visual configuration, no coding required, intuitive interface\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear mapping requirements or need to reshape data structure\n\n**Script-Based Transformation (`type: \"script\"`)**\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Capabilities**: Full programmatic control, custom processing, complex business rules\n- **Advantages**: Maximum flexibility, can implement any transformation logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Visual transformation tools aren't sufficient for your use case\n","properties":{"type":{"type":"string","description":"Determines which transformation mechanism to use. This choice affects which properties\nmust be configured and how transformation logic is implemented.\n\n**Available types**\n\n**Rule-Based Transformation (`\"expression\"`)**\n- **Required Config**: The `expression` object with mapping definitions\n- **Behavior**: Applies declarative rules to reshape data\n- **Best For**: Most transformation scenarios from simple to complex\n- **Advantages**: Visual configuration, no coding required\n\n**Script-Based Transformation (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to transform data\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard data transformations, use `\"expression\"`\n2. For complex logic or specialized processing, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based transformations. This object enables reshaping data\nwithout requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define transformation rules that\ncan map, modify, and generate data elements.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Field mappings define how input data is transformed to target fields\n- Formulas can be used to calculate or generate new values\n- Lookups can enrich data by fetching related information\n- Mode determines how records are processed (create new or modify existing)\n","properties":{"version":{"type":"string","description":"Version of the expression format. Determines which rules\nproperty contains the transformation logic.\n","enum":["1","2"]},"rules":{"type":"array","description":"Transformation rules for version 1 expressions. An array of\nrule groups; each group is an array of field-mapping objects.\nMost transforms have a single group. Present when `version`\nis `\"1\"`. The output record contains ONLY the generated\nfields — every unmapped field is dropped (v1 has no\nequivalent of Transform 2.0's `modify` mode), and the\nrecord's trace key does not survive the rebuild.\n","items":{"type":"array","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path to read from. Supports multiple\nsyntaxes: bare field names (`id`), dot notation\n(`fulfillment.shipment_id`), slash-prefixed paths\nfor XML (`/FeedProcessingStatus`), wildcards (`*.id`,\n`*.[Internal ID]`), and array indexing (`SDF[0]`).\n"},"generate":{"type":"string","description":"Target field name to write to. Typically a bare name\n(`id`) or dot path (`SDF.Filter.ID`).\n"},"key":{"type":"string","description":"Auto-generated identifier for this rule, used by the\nUI to track individual rules for editing and reordering.\n"}},"required":["extract","generate"]}}},"rulesTwoDotZero":{"type":"object","description":"Configuration for version 2 transformation rules. This object contains the core logic\nfor how data is mapped, enriched, and transformed.\n\n**Capabilities**\n\nTransformation 2.0 provides:\n- Precise field mapping with JSONPath expressions\n- Support for deeply nested data structures\n- Formula-based field generation\n- Dynamic lookups for data enrichment\n- Multiple operating modes to fit different scenarios\n","properties":{"mode":{"type":"string","description":"Transformation mode that determines how records are handled during processing.\n\n**Available modes**\n\n**Create Mode (`\"create\"`)**\n- **Behavior**: Builds entirely new output records from inputs\n- **Use When**: Output structure differs significantly from input\n- **Advantage**: Clean slate approach, no field inheritance\n\n**Modify Mode (`\"modify\"`)**\n- **Behavior**: Makes targeted edits to existing records\n- **Use When**: Output structure should remain similar to input\n- **Advantage**: Preserves unmapped fields from the original record\n","enum":["create","modify"]},"mappings":{"$ref":"#/components/schemas/Mappings"},"lookups":{"allOf":[{"description":"Shared lookup tables used across all mappings defined in the transformation rules.\n\n**Purpose**\n\nLookups provide centralized value translation that can be referenced from any mapping\nin your transformation configuration. They enable consistent translation of codes, IDs,\nand values between systems without duplicating translation logic.\n\n**Usage in transformations**\n\nLookups are particularly valuable in transformations for:\n\n- **Data Normalization**: Standardizing values from diverse source systems\n- **Code Translation**: Converting between different coding systems (e.g., status codes)\n- **Field Enrichment**: Adding descriptive values based on ID or code lookups\n- **Cross-Reference Resolution**: Mapping identifiers between integrated systems\n\n**Implementation**\n\nLookups are defined once in this array and referenced by name in mappings:\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"statusMapping\",\n    \"map\": {\n      \"A\": \"Active\",\n      \"I\": \"Inactive\",\n      \"P\": \"Pending\"\n    },\n    \"default\": \"Unknown Status\"\n  }\n]\n```\n\nThen referenced in mappings using the lookupName property:\n\n```json\n{\n  \"generate\": \"status\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.statusCode\",\n  \"lookupName\": \"statusMapping\"\n}\n```\n\nThe system automatically applies the lookup during transformation processing.\n\nFor complete details on lookup properties and behavior, see the Lookups schema.\n"},{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the JSON shape the transformTwoDotZero processor\nevaluates `mappings[].extract` JSONPath values against at\nflow runtime. Applies only to Transform 2.0 (v2,\n`rulesTwoDotZero`); v1 transforms (the `rules` array on\n`transform.expression.rules`) and script-mode transforms\nignore this field.\n"}}}}},"script":{"type":"object","description":"Configuration for programmable script-based transformations. This object enables complex, custom\ntransformation logic beyond what expression-based transformations can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to transform data according to\nspecialized business rules or complex algorithms.\n\n**Implementation approach**\n\nScript-based transformation works by:\n1. Executing the specified function from the referenced script\n2. Passing input data to the function\n3. Using the function's return value as the transformed output\n\n**Common use cases**\n\nScript transformation is ideal for:\n- Complex business logic that can't be expressed through mappings\n- Algorithmic transformations requiring computation\n- Dynamic transformations based on external factors\n- Legacy system data format compatibility\n- Multi-stage processing with intermediate steps\n\nOnly use script-based transformation when expression-based transformation is insufficient.\nScript transformation requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to a predefined script resource containing the transformation logic.\n\nThe referenced script should contain the function specified in the\n'function' property. It must be a script in this account: any other id fails the save\nwith 422 (\"Only a script owned by the user or a script shared by the Integration App can\nbe used at transform.script._scriptId.\").\n","format":"objectid"},"function":{"type":"string","description":"Name of the function within the script to execute for transformation. This function\nmust exist in the script referenced by _scriptId.\n"}}}}},"Lookups":{"type":"array","description":"Configuration for value-to-value transformations using lookup tables.\n\n**Purpose**\n\nLookups provide a way to translate values from one system to another. They transform\ninput values into output values using either static mapping tables or\ndynamic lookup caches.\n\n**Lookup mechanisms**\n\nThere are two distinct lookup mechanisms available:\n\n1. **Static Lookups**: Define a simple key-value map object and store it as part of your resource\n   - Best for: Small, fixed sets of values that rarely change\n   - Implementation: Configure the `map` object with input-to-output value mappings\n   - Example: Country codes, status values, simple translations\n\n2. **Dynamic Lookups**: Reference an existing 'Lookup Cache' resource in your Celigo account\n   - Best for: Large datasets, frequently changing values, or complex reference data\n   - Implementation: Configure `_lookupCacheId` to reference cached data maintained independently\n   - Example: Product catalogs, customer databases, pricing information\n\n**Property usage**\n\nThere are two mutually exclusive ways to configure lookups, depending on which mechanism you choose:\n\n1. **For Static Mappings**: Configure the `map` property with a direct key-value object\n   ```json\n   \"map\": {\"US\": \"United States\", \"CA\": \"Canada\"}\n   ```\n\n2. **For Dynamic Lookups**: Configure the following properties:\n   - `_lookupCacheId`: Reference to the lookup cache resource\n   - `extract`: JSON path to extract specific value from the returned lookup object\n\n**When to use**\n\nLookups are ideal for:\n\n1. **Value Translation**: Mapping codes or IDs to human-readable values\n\n2. **Data Enrichment**: Adding related information to records during processing\n\n3. **Normalization**: Ensuring consistent formatting of values across systems\n\n**Implementation details**\n\nLookups can be referenced in:\n\n1. **Field Mappings**: Direct use in field transformation configurations\n\n2. **Handlebars Templates**: Use within templates with the syntax:\n   ```\n   {{lookup 'lookupName' record.fieldName}}\n   ```\n\n**Example usage**\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"countryCodeToName\",\n    \"map\": {\n      \"US\": \"United States\",\n      \"CA\": \"Canada\",\n      \"UK\": \"United Kingdom\"\n    },\n    \"default\": \"Unknown Country\",\n    \"allowFailures\": true\n  },\n  {\n    \"name\": \"productDetails\",\n    \"_lookupCacheId\": \"60a2c4e6f321d800129a1a3c\",\n    \"extract\": \"$.details.price\",\n    \"allowFailures\": false\n  }\n]\n```\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for the lookup table within this configuration.\n\nThis name must be unique within the scope where the lookup is defined and is used to reference\nthe lookup in handlebars templates with the syntax {{lookup 'name' value}}.\n\nChoose descriptive names that indicate the transformation purpose, such as:\n- \"countryCodeToName\" for country code to full name conversion\n- \"statusMapping\" for status code translations\n- \"departmentCodes\" for department code to name mapping\n"},"map":{"type":["object","null"],"description":"The lookup mapping table as key-value pairs. The platform stores `null`\nhere on dynamic lookups, which resolve values at runtime instead of\nfrom a static table.\n\nThis object contains the input values as keys and their corresponding\noutput values. When a input value matches a key in this object,\nit will be replaced with the corresponding value.\n\nThe map should be kept to a reasonable size (typically under 100 entries)\nfor optimal performance. For larger mapping requirements, consider using\ndynamic lookups instead.\n\nMaps can include:\n- Simple code to name conversions: {\"US\": \"United States\"}\n- Status transformations: {\"A\": \"Active\", \"I\": \"Inactive\"}\n- ID to name mappings: {\"100\": \"Marketing\", \"200\": \"Sales\"}\n\nValues can be strings, numbers, or booleans, but all are stored as strings\nin the configuration.\n"},"_lookupCacheId":{"type":"string","description":"Reference to a LookupCache resource that contains the reference data for the lookup.\n\n**Purpose**\n\nThis field connects the lookup to an external data source that has been cached in the system.\nUnlike static lookups that use the `map` property, dynamic lookups can reference large datasets\nor frequently changing information without requiring constant updates to the integration.\n\n**Implementation details**\n\nThe LookupCache resource referenced by this ID contains:\n- The data records to be used as a reference source\n- Configuration for how the data should be indexed and accessed\n- Caching parameters to balance performance with data freshness\n\n**Usage patterns**\n\nCommonly used to reference:\n- Product catalogs or SKU databases\n- Customer or account information\n- Pricing tables or discount rules\n- Complex business logic lookup tables\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n","format":"objectid"},"extract":{"type":"string","description":"JSON path expression that extracts a specific value from the cached lookup object.\n\n**Purpose**\n\nWhen using dynamic lookups with a LookupCache, this JSON path identifies which field to extract\nfrom the cached object after it has been retrieved using the lookup key.\n\n**Implementation details**\n\n- Must use JSON path syntax (similar to mapping extract fields)\n- Operates on the cached object returned by the lookup operation\n- Examples:\n  - \"$.name\" - Extract the name field from the top level\n  - \"$.details.price\" - Extract a nested price field\n  - \"$.attributes[0].value\" - Extract a value from the first element of an array\n\n**Usage scenario**\n\nWhen a lookup cache contains complex objects:\n```json\n// Cache entry for key \"PROD-123\":\n{\n  \"id\": \"PROD-123\",\n  \"name\": \"Premium Widget\",\n  \"details\": {\n    \"price\": 99.99,\n    \"currency\": \"USD\",\n    \"inStock\": true\n  }\n}\n```\n\nSetting extract to \"$.details.price\" would return 99.99 as the lookup result.\n\nIf no extract is provided, the entire cached object is returned as the lookup result.\n"},"default":{"type":["string","null"],"description":"Default value to use when the source value is not found in the lookup map.\nThe platform stores `null` here when no default is configured.\n\nThis value is used as a fallback when:\n1. The source value doesn't match any key in the map\n2. allowFailures is true or omitted\n\nSetting an appropriate default helps prevent flow failures due to unexpected\nvalues and provides predictable behavior for edge cases.\n\nCommon default patterns include:\n- Descriptive unknowns: \"Unknown Country\", \"Unspecified Status\"\n- Original value indicators: \"{Original Value}\", \"No mapping found\"\n- Neutral values: \"Other\", \"N/A\", \"Miscellaneous\"\n\nA save refuses a default together with an explicit `allowFailures: false`\n(422 `Lookups with default set can not have allowFailures explicitly set to\nfalse.`): a lookup either fails a miss (`allowFailures: false`, no default) or\nfalls back to its default.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, missing lookup values will use the default value rather than causing an error.\n\n**Behavior control**\n\nThis field determines how the system handles source values that don't exist in the map:\n\n- true: Use the default value for missing mappings and continue processing\n- false: Treat missing mappings as errors, failing the record (`lookup_error` for a\n  static map, `lookupcache_evaluation_failed` for a lookup cache)\n- omitted or null: the record doesn't fail either. On an import a static lookup writes\n  its default if it has one, and a static or cache lookup without one leaves the\n  mapped field out of the output, with no error\n\n**Recommendation**\n\nSet this to true when:\n- New source values might appear over time\n- Data quality issues could introduce unexpected values\n- Processing should continue even with imperfect mapping\n\nSet this to false when:\n- Complete data accuracy is critical\n- All possible source values are known and controlled\n- Missing mappings indicate serious data problems that should be addressed\n\nA default raises no error: records that miss go out with the default value and\nnothing reports them. Use true only when that fallback is acceptable at the\ndestination, and set false explicitly when a missing value must stop the record.\n"}}}},"Form":{"type":"object","description":"Configuration for creating user-friendly settings forms that make it easier for less technical users\nto configure integration resources.\n\n**Settings form builder**\n\nThe Settings Form Builder allows you to create or edit user-friendly fields that prompt for text entry\nor selections that will be returned as settings applied to this resource. Your forms can include any\nfield types that you see elsewhere in integrator.io, such as:\n\n- Text fields\n- Dropdown selections\n- Checkboxes\n- Radio buttons\n- Date pickers\n- Multi-select fields\n- Search fields\n\nForm fields make it much easier for less technical users to work with your integration settings by:\n\n- Providing clear labels and help text\n- Enforcing validation rules\n- Offering pre-defined selection options\n- Grouping related settings logically\n- Supporting conditional visibility\n- Creating a consistent user experience\n","properties":{"form":{"type":"object","description":"Configuration that defines the structure, fields, and behavior of the settings form.\n\nThis object contains the complete definition of the form's layout, fields, validation rules,\nand interactive behaviors. The specific structure depends on the form complexity and can include\nfield definitions, sections, conditional display logic, and default values.\n\nThe form configuration is typically created and managed through the visual Form Builder interface\nrather than edited directly as JSON.\n","properties":{"fieldMap":{"type":"object","description":"A mapping of field identifiers to their configuration objects.\nEach key in this object represents a unique field ID, and the value contains\nall the configuration settings for that specific form field.\n","additionalProperties":{"type":"object","description":"Configuration for an individual form field.\n","properties":{"id":{"type":"string","description":"Unique identifier for this field within the form: layout containers and conditions\nrefer to the field by it. Use the same string as the field's fieldMap key and its `name`.\n"},"name":{"type":"string","description":"Key the field's value is saved under: one top-level key of the resource's `settings`,\nwhich the form also reads the value back from when it opens. Nested objects inside\n`settings` are written to it directly, not through a form field's `name`.\n"},"type":{"type":"string","description":"The type of form control to render for this field.\n","enum":["text","checkbox","radiogroup","relativeuri","editor","keyvalue","select","multiselect","toggle","datetime","date","exportSelect","staticMap"]},"label":{"type":"string","description":"Display label shown next to the field in the form.\n"},"description":{"type":"string","description":"Detailed explanation text that appears below the field, providing more context\nthan the label or helpText.\n"},"helpText":{"type":"string","description":"Explanatory text that appears when hovering over the help icon next to the field.\nUsed to provide additional guidance on how to use the field.\n"},"required":{"type":"boolean","description":"When true, the field must have a value before the form can be submitted. Only the form\nenforces it: an API write of `settings` is stored without the value.\n","default":false},"defaultValue":{"description":"Value the form shows while the resource's `settings` holds no value under the field's\n`name`.\n"},"multiline":{"type":"boolean","description":"For text fields, determines whether the input should be a multi-line text area\ninstead of a single-line input.\n","default":false},"rowsMax":{"type":"integer","description":"For multiline text fields, specifies the maximum number of visible rows.\n"},"inputType":{"type":"string","description":"For text fields, specifies the HTML input type attribute to apply additional\nvalidation or specialized input behavior.\n","enum":["text","number","email","password","tel","url"]},"delimiter":{"type":"string","description":"For text fields, specifies a character to use for splitting the input into an array.\nUsed for collecting multiple values in a single text field.\n"},"mode":{"type":"string","description":"For editor fields, specifies the type of content being edited for syntax highlighting.\n","enum":["json","xml","csv","text"]},"keyName":{"type":"string","description":"For keyvalue and staticMap fields, names the first column: each saved row holds that\ncolumn's value under this key. A keyvalue field without it uses `key`.\n"},"valueName":{"type":"string","description":"For keyvalue and staticMap fields, names the second column: each saved row holds that\ncolumn's value under this key. A keyvalue field without it uses `value`.\n"},"keyLabel":{"type":"string","description":"For staticMap fields, heading of the first column.\n"},"valueLabel":{"type":"string","description":"For staticMap fields, heading of the second column.\n"},"keyOptions":{"type":"array","description":"For staticMap fields, suggestions offered in the first column's cells. A cell still\naccepts a typed value.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"Display text for the suggestion.\n"},"value":{"type":"string","description":"Value saved when the suggestion is chosen.\n"}}}},"valueOptions":{"type":"array","description":"For staticMap fields, suggestions offered in the second column's cells. A cell still\naccepts a typed value.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"Display text for the suggestion.\n"},"value":{"type":"string","description":"Value saved when the suggestion is chosen.\n"}}}},"keyResource":{"type":"object","description":"For staticMap fields, fills the first column's suggestions from an export the form\ncarries inline in `virtual`, run the way an exportSelect's `resource` is. With\n`keyResource` or `valueResource` set, the form requires both columns in every row\nunless `keyValueRequired` is false.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline, shaped like an exportSelect's `resource.virtual`.\n","additionalProperties":true}}},"valueResource":{"type":"object","description":"For staticMap fields, fills the second column's suggestions from an export the form\ncarries inline in `virtual`, run the way an exportSelect's `resource` is.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline, shaped like an exportSelect's `resource.virtual`.\n","additionalProperties":true}}},"keyValueRequired":{"type":"boolean","description":"For staticMap fields with `keyResource` or `valueResource`, when false, saves rows\nthat fill only one column; by default the form drops them.\n","default":true},"resource":{"type":"object","description":"For exportSelect fields, the export whose records become the options. When the form\nopens, the UI previews the export in `virtual` (with `test.limit` 100 unless the export\nsets its own) and lists the preview's `data`: each record needs a `label` and a `value`\n(a string or number serves as both), so shape them with the export's `transform`. A\nsaved export can't be referenced, and an HTTP export that finds no records leaves the\nfield showing an error instead of options.\n","properties":{"virtual":{"type":"object","description":"Export document to run, inline: `adaptorType`, its adaptor block, and an optional\n`transform`. `_connectionId` defaults to the connection of the export or import the\nform belongs to; set it on any other resource's form.\n","additionalProperties":true}}},"multiselect":{"type":"boolean","description":"For exportSelect fields, when true, the user can choose several options and the value\nis saved as an array.\n","default":false},"supportHandlebars":{"type":"boolean","description":"For exportSelect fields, when true, renders the `resource.virtual` export as a Handlebars\ntemplate before each preview (all but its `transform`, `hooks`, and `filter`). On an\nintegration, flow group, flow, export, or import form, the form's current values are\nunder that form's scope name (`integration`, `flowGrouping`, `flow`, `export`, or\n`import`), beside the saved settings of the scopes above it, with no `settings.`\nprefix; on a connection or iClient form the template has no context. The options then\nload through `refreshOptionsOnChangesTo` or `typeAheadSearch`, or when the user\nrefreshes the field.\n","default":false},"refreshOptionsOnChangesTo":{"type":"array","items":{"type":"string"},"description":"For exportSelect fields with `supportHandlebars`, ids of the fields whose values the\ntemplated export uses: the options load once one of them has a value and reload whenever\none changes.\n"},"typeAheadSearch":{"type":"boolean","description":"For exportSelect fields, when true, re-runs the templated export as the user types, with\nthe typed text as this field's own value; an empty box clears the options, and typed\ntext is accepted as the value.\n","default":false},"showDelete":{"type":"boolean","description":"For keyvalue fields, determines whether to show a delete button for each key-value pair.\n"},"doNotAllowFutureDates":{"type":"boolean","description":"For date and datetime fields, restricts selection to dates not in the future.\n"},"skipTimezoneConversion":{"type":"boolean","description":"For datetime fields, prevents automatic timezone conversion of the date value.\n"},"options":{"type":"array","description":"For fields that present choices (select, multiselect, radiogroup, toggle), defines\nthe available options.\n","items":{"anyOf":[{"title":"Option group","type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"oneOf":[{"title":"String value","type":"string"},{"title":"Label-value pair","type":"object","properties":{"label":{"type":"string","description":"Display text for the option.\n"},"value":{"type":"string","description":"Value to store when this option is selected.\n"}}}]},"description":"Array of option values/labels to display in the selection control.\n"}}},{"title":"Label-value pair","type":"object","required":["label"],"properties":{"label":{"type":"string","description":"Display text for the option.\n"},"value":{"type":"string","description":"Value to store when this option is selected.\n"}}}]}},"visibleWhen":{"type":"array","description":"Conditional display rules that determine when this field should be visible.\nIf empty or not provided, the field is always visible.\n","items":{"type":"object","properties":{"field":{"type":"string","description":"The ID of another field whose value controls the visibility of this field. The\nsave doesn't check it: a rule naming an ID that isn't in the form counts as met,\nso the field stays visible.\n"},"is":{"type":"array","items":{"type":["string","boolean","number","null"]},"description":"Array of values - if the referenced field has any of these values,\nthis field will be visible. Values may be strings, booleans (for\ncheckbox/toggle fields), numbers (for numeric inputs), or null.\n"}}}}}}},"layout":{"type":"object","description":"Defines how the form fields are arranged and grouped in the UI.\nThe layout can organize fields into columns, sections, or other visual groupings.\n","properties":{"type":{"type":"string","description":"The type of layout to use for the form.\n","enum":["column","collapse","box","indent","tabWithoutSave","verticalTabWithoutSave"]},"fields":{"type":"array","items":{"type":"string"},"description":"Field IDs shown above the containers, without a heading.\n"},"containers":{"type":"array","description":"Array of container objects that group fields or contain nested containers.\nEach container can represent a column, box, indented section, or collapsible section.\n","items":{"type":"object","properties":{"type":{"type":"string","description":"The visual style of the container.\n","enum":["indent","box","collapse"]},"label":{"type":"string","description":"The heading text displayed for this container.\n"},"fields":{"type":"array","items":{"type":"string"},"description":"Array of field IDs that should be displayed in this container.\nEach ID must correspond to a key in the fieldMap object: the save doesn't check it,\nand an ID that doesn't renders nothing. A field the layout doesn't list isn't shown.\n"},"containers":{"type":"array","description":"Nested containers within this container. Allows for hierarchical organization\nof fields with different visual styles.\n","items":{"type":"object","properties":{"label":{"type":"string","description":"The heading text displayed for this nested container.\n"},"fields":{"type":"array","items":{"type":"string"},"description":"Array of field IDs that should be displayed in this nested container.\n"}}}}}}}}}},"additionalProperties":true},"init":{"type":"object","description":"Configuration for custom JavaScript initialization that executes when the form is first loaded.\n\nThis object defines a JavaScript hook that prepares the form for use, sets initial field values,\nperforms validation, or otherwise customizes the form behavior before it is displayed to the user.\n\n**Function signature**\n\nThe initialization function is invoked with a single 'options' argument containing contextual information:\n```javascript\nfunction formInit(options) {\n  // Process options and return the form object\n  return options.resource.settingsForm.form;\n}\n```\n\n**Available context**\n\nThe 'options' argument provides access to:\n- `options.resource` - The current resource being configured\n- `options.parentResource` - The parent of the current resource\n- `options.grandparentResource` - The grandparent of the current resource\n- `options.license` - For integration apps, the license provisioned to the integration\n- `options.parentLicense` - For integration apps, the parent of the license\n- `options.sandbox` - Boolean flag indicating whether the script is running in a sandbox environment\n\n\n**Common uses**\n\n- Dynamically generate field options based on resource configuration\n- Pre-populate default values from related resources\n- Apply conditional logic that depends on resource properties\n- Add, remove, or modify form fields based on user permissions or account settings\n- Fetch external data to populate selection options\n- Implement complex validation rules that depend on resource context\n- Create branching form experiences based on user selections\n\n**Return value**\n\nThe function must return a valid form object that the UI can render.\nThrowing an exception will signal an error to the user.\n\nThe UI runs the function each time it opens the form and renders the returned form instead of\n`form`, then fills in the values from `settings`; a thrown error's message replaces the form.\n","properties":{"function":{"type":"string","description":"The name of the function to execute within the referenced script.\n\nThis property specifies which function to invoke from the script\nreferenced by _scriptId. The function will be called when the form\nis initialized and should handle any custom setup logic.\n\nThe function must follow the expected signature and return a valid form object.\n"},"_scriptId":{"type":"string","format":"objectId","description":"Reference to a predefined script resource containing the initialization function.\n\nThe referenced script should contain the function specified in the\n'function' property. On an integration, a flow group, a flow, an export, an import, or a\nconnection, an id that isn't a script in the same account (or, on an Integration App's\nresources, one the app shares) is refused with 422 `invalid_ref`. An iClient save\nstores the id without that check.\n"}}}}},"Settings":{"type":"object","description":"Configuration settings that can be accessed by hooks, filters, mappings and handlebars templates at runtime.\n\nIt enables customization of the resource's logic, allowing hooks, mappings, filters, and\nhandlebars to access and apply the settings at runtime.\n\n**Usage**\n\nThe settings object can store arbitrary JSON data that you want to save with the resource.\nWhile it's often populated through a form defined in the `settingsForm` field, you can also:\n\n- Directly provide JSON settings without using a form\n- Store configuration values used by hooks and templates\n- Create resource-specific constants and parameters\n- Maintain lookup tables or mapping structures\n- Define conditional logic parameters\n\n**Accessibility**\n\nSettings are available in:\n- All handlebars fields for building dynamic payloads\n- Field mapping expressions\n- JavaScript hooks via the options object\n- Filters and transformations\n\nAt runtime, the settings objects in the step's execution chain are gathered into a\nsingle `settings` context keyed by fixed scope keys. Flow runs populate the full\nchain: `settings.integration.*`, `settings.flowGrouping.*` (the flow group the flow\nbelongs to), `settings.flow.*`, `settings.connection.*`, `settings.iClient.*`, and\nthe running step's own scope — `settings.export.*` on exports/lookups,\n`settings.import.*` on imports (the key matches the step type). Steps executing\ninside a My API or a Tool receive only the step's own scope plus\n`settings.connection.*` / `settings.iClient.*`; the integration, flowGrouping, and\nflow scopes resolve empty there. The scope key is literal — resource display names\nand settingsForm section labels are never part of the path. The segments after the\nscope mirror the stored settings JSON key path exactly: a top-level field is\n`settings.<scope>.<fieldId>`, and nested objects add one segment per JSON key\n(e.g. `settings.flowGrouping.Customer.region` when the group's settings JSON nests\n`region` under `Customer`). References without a scope key\n(e.g. `{{settings.myField}}`) resolve to empty strings.\n\n**Best practices**\n\nFor non-technical users, create a custom form instead of editing the JSON directly.\nThis provides a user-friendly interface for updating settings without requiring JSON knowledge.\n","additionalProperties":true},"MockOutput":{"type":["object","null"],"description":"Sample data that simulates the output from an export for testing and configuration purposes.\n\nMock output allows you to configure and test flows without executing the actual export or\nwaiting for real-time data to arrive. This is particularly useful for:\n- Initial flow configuration and testing\n- Mapping development without requiring live data\n- Generating metadata for downstream flow steps\n- Creating realistic test scenarios\n- Documenting expected data structures\n\n**Structure**\n\nThe mock output must follow the integrator.io canonical format, which consists of a\n`page_of_records` array containing record objects. Each record object has a `record`\nproperty that contains the actual data fields.\n\n```json\n{\n  \"page_of_records\": [\n    {\n      \"record\": {\n        \"field1\": \"value1\",\n        \"field2\": \"value2\",\n        ...\n      }\n    },\n    ...\n  ]\n}\n```\n\n**Usage**\n\nIn a flow test run, and for an upstream lookup during a page-processor preview\n(`previewPageProcessor`), integrator.io substitutes this mock output for the export's live\nresponse instead of calling the source. A saved mock is not read anywhere else: a target\nlookup in that preview runs its live query, invoking the export\n(`POST /v1/exports/{_id}/invoke`) reads the source, and real runs ignore it. This allows you\nto:\n- Test mappings with representative data\n- Configure downstream flow steps without waiting for real data\n- Simulate various data scenarios\n\nWhere it is substituted, the records stand in for the RAW parsed response: the export's own\n`transform` and output `filter` still run on top of them, so downstream steps — and a\nlookup's results mapping — see the transformed, filtered records, not the shape stored here.\n\nIn a flow test run of an HTTP export with a saved mock (live probe, 2026-10-21), the export\nstill listed `request` and `response` stages: `request` held the request the export composes,\nand `response` held the mock under the source's URL, as did the test run's request log, which\nrecorded it as a 200. Read those stages as the substituted mock, not as a live call.\n\n`POST /v1/exports/preview` reads the `mockOutput` in its request body, not the saved one. For\nan export the preview supports (an HTTP export, for example), a body that carries it (as an\nexport read back with GET does) returns that snapshot as a lone `parse` stage without calling\nthe source; remove `mockOutput` from the body to preview live records.\n\n**Limitations**\n\n- Maximum of 10 records\n- Maximum size of 1 MB\n- Must follow the canonical format shown above\n\nMock output can be populated automatically from preview data (which is the parsed response,\nbefore `transform` / `filter`) or entered manually.\n","properties":{"page_of_records":{"type":"array","description":"Array of record objects in the integrator.io canonical format.\n\nEach item in this array represents one record that would be processed\nby the flow during execution.\n","items":{"type":"object","properties":{"record":{"type":"object","description":"Container for the actual record data fields.\n\nThe structure of this object will vary depending on the specific\nexport configuration and the source system's data structure.\n","additionalProperties":true}}}}}},"PreSave":{"type":"object","description":"Defines a JavaScript hook that executes before the resource is saved.\n\nThis hook allows for programmatic validation, transformation, or enrichment of the\nresource itself before it is persisted. It can be used to enforce business rules,\nset derived properties, or implement cross-field validations that can't be expressed\nthrough the standard UI.\n\n**Function signature**\n\nThe preSave function is invoked with a single 'options' argument containing:\n```javascript\nfunction preSave(options) {\n  // Process options and return the modified resource\n  return options.newResource;\n}\n```\n\n**Available context**\n\nThe 'options' argument provides access to:\n- `options.newResource` - The resource being saved (with pending changes)\n- `options.oldResource` - The previous version of the resource (before changes)\n- `options.sandbox` - Boolean flag indicating whether the script is running in a sandbox environment\n\n\n**Common uses**\n\n- Enforcing complex business rules across multiple fields\n- Automatically deriving field values based on other configuration\n- Performing validation that depends on external systems or data\n- Normalizing or standardizing configuration values\n- Adding computed or derived properties\n- Implementing versioning or change tracking\n- Dynamically looking up data using the Celigo API module to enrich configuration\n\n**Return value**\n\nThe function must return the newResource object (potentially modified) to be saved.\nThrowing an exception will prevent saving and signal an error to the user.\n","properties":{"function":{"type":"string","description":"The name of the function to execute within the referenced script.\n\nThis property specifies which function to invoke from the script\nreferenced by _scriptId. The function will be called just before\nthe resource is saved.\n\nThe function must follow the expected signature and return the resource object.\n"},"_scriptId":{"type":"string","format":"objectId","description":"Reference to a predefined script resource containing the preSave function.\n\nThe referenced script should contain the function specified in the\n'function' property. This script must be accessible within the user's account\nand have appropriate permissions.\n"}}},"ResourceResponse":{"type":"object","description":"Response","properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Unique identifier for the resource. Format is a 24-character hexadecimal string."},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was created. Set automatically and cannot be modified."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was last updated. Changes whenever any property is modified."},"deletedAt":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp when the resource was soft-deleted. When null or absent, the resource is active."}},"required":["_id"]},"IAResourceResponse":{"type":"object","description":"Integration app response fields for resources that are part of integration apps","properties":{"_integrationId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the specific integration instance that contains this resource.\n\nThis field is only populated for resources that are part of an integration app\ninstallation. It contains the unique identifier (_id) of the integration\nresource that was installed in the account.\n\nThe integration instance represents a specific installed instance of an\nintegration app, with its own configuration, settings, and runtime environment.\n\nThis reference enables:\n- Tracing the resource back to its parent integration instance\n- Permission and access control based on integration ownership\n- Lifecycle management (enabling/disabling, updating, or uninstalling)\n"},"_connectorId":{"type":"string","format":"objectId","readOnly":true,"description":"Integration App (connector) that defines this resource. Present only on resources an\nIntegration App owns. The app locks parts of these documents, and a PUT that changes a\nlocked field answers 200 but keeps the stored value, so read the document back to confirm\na change."}}},"AIDescription":{"type":"object","description":"AI-generated descriptions and documentation for the resource.\n\nThis object contains automatically generated content that helps users\nunderstand the purpose, behavior, and configuration of the resource without\nrequiring them to analyze the technical details. The AI-generated content\nis sanitized and safe for display in the UI.\n","properties":{"summary":{"type":["string","null"],"description":"Brief AI-generated summary of the resource's purpose and functionality.\n\nThis concise description provides a quick overview of what the resource does,\nwhat systems it interacts with, and its primary role in the integration.\nThe summary is suitable for display in list views, dashboards, and other\ncontexts where space is limited.\n\nMaximum length: 10KB\n"},"detailed":{"type":["string","null"],"description":"Comprehensive AI-generated description of the resource's functionality.\n\nThis detailed explanation covers the resource's purpose, configuration details,\ndata flow patterns, filtering logic, and other technical aspects. It provides\nin-depth information suitable for documentation, tooltips, or detailed views\nin the administration interface.\n\nThe content may include HTML formatting for improved readability.\n\nMaximum length: 10KB\n"},"generatedOn":{"type":["string","null"],"format":"date-time","description":"Timestamp indicating when the AI description was generated.\n\nThis field helps track the freshness of the AI-generated content and\ndetermine when it might need to be regenerated due to changes in the\nresource's configuration or behavior.\n\nThe timestamp is recorded in ISO 8601 format with UTC timezone (Z suffix).\n"}}},"APIM":{"type":"array","description":"Read-only field that stores information about the integration resources\npublished in the API Management (APIM) platform.\n\nThis field tracks the relationship between integrator.io resources and their\npublished counterparts in the APIM platform, which is\ntightly integrated with the Celigo UI. When resources are \"pushed\" to APIM,\nthis field is populated with the relevant identifiers and statuses.\n","items":{"type":"object","properties":{"apiId":{"type":"string","description":"Identifier for the API where this integrator.io resource is published in the APIM.\n\nThis is an APIM resource identifier (not prefixed with underscore like Celigo IDs)\nthat uniquely identifies the API in the API Management platform.\n"},"flowId":{"type":"string","description":"Identifier for the flow within the API where this integrator.io resource is linked.\n\nWhen an API has multiple integrator.io resources linked, each resource is associated\nwith a specific flow in the API, identified by this field. This is an APIM\nresource identifier.\n"},"status":{"type":"string","description":"Indicates the publishing stage of the integrator.io resource in APIM.\n\nPossible values:\n- 'oaspending': The resource is published but the OpenAPI Specification (OAS) is not\n  yet published. The apiId will be updated with the API ID created in APIM.\n- 'published': The OpenAPI Specification for the integrator.io resource has been\n  successfully uploaded to APIM.\n","enum":["oaspending","published"]}}}},"PreviewResponse":{"type":"object","description":"Response body for export data preview","properties":{"data":{"type":"array","description":"The data exported from source app","items":{"type":"object","description":"Individual data record from the source app"}},"dataURIs":{"type":"array","description":"URIs to the data in the source app","items":{"type":"string","description":"URI to a specific data record in the source app"}},"stages":{"type":"array","description":"Processing stages information","items":{"type":"object","description":"Information about a processing stage","properties":{"name":{"type":"string","description":"Name of the processing stage"},"errors":{"type":"array","description":"Errors encountered during this stage","items":{"type":"object","description":"Error information"}},"data":{"type":"array","description":"Data after being processed by this stage","items":{"type":"object","description":"Individual data record after being processed by this stage"}}}}}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"400-bad-request":{"description":"Bad request. The server could not understand the request because of malformed syntax or invalid parameters.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-unprocessable-entity":{"description":"Unprocessable entity. The request was well-formed but was unable to be followed due to semantic errors.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/integrations/{_integrationId}/flows/{_flowId}/exports/preview":{"post":{"summary":"Preview export data","description":"Preview export data from a specific export within a flow. This endpoint allows you to\npreview the data that would be exported, including the exported data, URIs to the data\nin the source app, and processing stages information.\n","operationId":"previewExportData","tags":["Exports"],"parameters":[{"name":"_integrationId","in":"path","required":true,"description":"The integration ID","schema":{"type":"string","format":"objectId"}},{"name":"_flowId","in":"path","required":true,"description":"The flow ID","schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewRequest"}}}},"responses":{"200":{"description":"Successfully previewed export data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewResponse"}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
````

## Invoke an export and return its data

> Runs an existing export end-to-end and returns its records (or errors)\
> synchronously. Unlike \`POST /v1/flows/{\_id}/run\`, which starts a full flow\
> job, this endpoint invokes a \*\*single export\*\* in isolation and returns the\
> result directly in the response body.\
> \
> The request body is optional — pass \`{}\` or omit the body entirely for\
> exports that require no input. Some adaptor types accept a \`data\` array in\
> the body to supply input records.\
> \
> On success, the response contains the records as the export hands them\
> on: after its transform, output filter and preSavePage hook, so records\
> come back in their final shape and records the filter or hook drops aren't\
> returned. On\
> application-level failure (e.g. the source system is unreachable), the\
> endpoint still returns a successful HTTP status with the errors in an\
> \`errors\` array — higher-level error codes are reserved for request-level\
> validation (bad ID, missing auth).\
> \
> This endpoint executes the export against the live source system.\
> \`POST /v1/exports/preview\` also queries the live source when its body\
> carries no \`mockOutput\` (reads only, no job) — prefer it when you only\
> need to inspect fetched records.

```json
{"openapi":"3.2.0","info":{"title":"Exports","version":"1.0.0"},"tags":[{"name":"Exports","description":"Exports retrieve data from source systems — on a schedule, in delta mode, in real time,\non demand, or as a file/blob transfer — package the results into ≤ 5-MB pages, and pass\neach page to downstream flow steps. Depending on configuration, an export surfaces in the\nFlow Builder as an export, a real-time listener, a file transfer, or a mid-flow lookup.\n\n## Export schema\n\n{% openapi-schemas spec=\"export\" schemas=\"Export\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}}}},"paths":{"/v1/exports/{_id}/invoke":{"post":{"operationId":"invokeExport","tags":["Exports"],"summary":"Invoke an export and return its data","description":"Runs an existing export end-to-end and returns its records (or errors)\nsynchronously. Unlike `POST /v1/flows/{_id}/run`, which starts a full flow\njob, this endpoint invokes a **single export** in isolation and returns the\nresult directly in the response body.\n\nThe request body is optional — pass `{}` or omit the body entirely for\nexports that require no input. Some adaptor types accept a `data` array in\nthe body to supply input records.\n\nOn success, the response contains the records as the export hands them\non: after its transform, output filter and preSavePage hook, so records\ncome back in their final shape and records the filter or hook drops aren't\nreturned. On\napplication-level failure (e.g. the source system is unreachable), the\nendpoint still returns a successful HTTP status with the errors in an\n`errors` array — higher-level error codes are reserved for request-level\nvalidation (bad ID, missing auth).\n\nThis endpoint executes the export against the live source system.\n`POST /v1/exports/preview` also queries the live source when its body\ncarries no `mockOutput` (reads only, no job) — prefer it when you only\nneed to inspect fetched records.","parameters":[{"in":"path","name":"_id","required":true,"schema":{"type":"string","format":"objectId"},"description":"Export ID"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","description":"Optional input payload. Most exports ignore the body; some accept\na `data` array of records to feed into the export pipeline.","properties":{"data":{"type":"array","description":"Input records for the export (adaptor-dependent)","items":{"type":"object","additionalProperties":true}}},"additionalProperties":true}}}},"responses":{"200":{"description":"Export completed. The response contains either the export's records\n(after its transform, output filter and preSavePage hook) or an\n`errors` array if the export encountered application-level failures\n(connection timeout, file not found, etc.). Always inspect for errors\neven on 200.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"description":"Export not found","content":{"application/json":{"schema":{"type":"object","properties":{"errors":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"500":{"description":"Server error — can occur when the export is misconfigured (e.g. a\nSimpleExport with no connection)."}}}}}}
```

## List dependencies of an export

> Returns the set of resources that depend on the specified resource.\
> The response is an object whose keys are dependent-resource types\
> (e.g. \`flows\`, \`imports\`) and whose values are arrays of dependency\
> entries. Returns \`{}\` when no dependents exist, including for\
> well-formatted but nonexistent IDs.

```json
{"openapi":"3.2.0","info":{"title":"Exports","version":"1.0.0"},"tags":[{"name":"Exports","description":"Exports retrieve data from source systems — on a schedule, in delta mode, in real time,\non demand, or as a file/blob transfer — package the results into ≤ 5-MB pages, and pass\neach page to downstream flow steps. Depending on configuration, an export surfaces in the\nFlow Builder as an export, a real-time listener, a file transfer, or a mid-flow lookup.\n\n## Export schema\n\n{% openapi-schemas spec=\"export\" schemas=\"Export\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"DependencyResponse":{"type":"object","description":"Map of dependent-resource types to arrays of dependency entries.\nKeys are plural resource type strings (e.g. `flows`, `imports`,\n`connections`). An empty object `{}` means no dependents.\n","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/DependencyEntry"}}},"DependencyEntry":{"type":"object","description":"A single resource that depends on the queried resource.","properties":{"id":{"type":"string","description":"Unique identifier of the dependent resource."},"name":{"type":"string","description":"Display name of the dependent resource."},"type":{"type":"string","description":"Sub-type of the dependent resource when its collection distinguishes one, such as\n`stream` for a listener export. Only present for resource types that carry a `type`."},"paths":{"type":"array","description":"Dot-notation paths within the dependent resource that reference\nthe target resource. `[*]` denotes array elements.","items":{"type":"string"}},"accessLevel":{"type":"string","description":"The caller's access level on the dependent resource."},"dependencyIds":{"type":"object","description":"Map of resource types to arrays of IDs that this dependent\nresource references on the target. Keys are singular or plural\nresource type strings; values are arrays of ID strings.","additionalProperties":{"type":"array","items":{"type":"string"}}}},"required":["id","name","paths","accessLevel","dependencyIds"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}}}},"paths":{"/v1/exports/{_id}/dependencies":{"get":{"operationId":"listExportDependencies","tags":["Exports"],"summary":"List dependencies of an export","description":"Returns the set of resources that depend on the specified resource.\nThe response is an object whose keys are dependent-resource types\n(e.g. `flows`, `imports`) and whose values are arrays of dependency\nentries. Returns `{}` when no dependents exist, including for\nwell-formatted but nonexistent IDs.","parameters":[{"name":"_id","in":"path","required":true,"description":"Resource ID.","schema":{"type":"string","format":"objectId"}}],"responses":{"200":{"description":"Dependency map. Keys are resource-type strings; values are arrays\nof dependency entries. Returns `{}` when no dependents exist.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DependencyResponse"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"}}}}}}
```
