> 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/imports.md).

# Imports

Imports deliver data to destination systems. An import receives pages of records from upstream flow steps, applies field mappings and transformations, and writes the results to the target application, database, or file destination — handling lookups, duplicate avoidance, and per-record error reporting along the way. Each import uses one `adaptorType` that determines its connection compatibility and configuration object.

### Import schema

## The Import object

````json
{"openapi":"3.2.0","info":{"title":"Imports","version":"1.0.0"},"components":{"schemas":{"Import":{"type":"object","required":["_id","name","adaptorType","apiIdentifier","createdAt","lastModified"],"description":"Import object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ImportBase"},{"$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 import."},"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the source resource this import was created from."},"_templateId":{"type":"string","format":"objectId","readOnly":true,"description":"Template this import was created from."},"draft":{"type":"boolean","readOnly":true,"description":"When true, this import 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 import expires."},"debugUntil":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp until which debug logging is enabled for this import."}}}]},"ImportBase":{"type":"object","description":"Writable import fields shared by the request and response schemas.","properties":{"_connectionId":{"type":"string","format":"objectId","description":"Connection this import uses to reach the destination system. The connection's type must\nbe compatible with the import's `adaptorType` (e.g. an `HTTPImport` needs an `http`\nconnection; `NetSuiteDistributedImport` and `NetSuiteHTTPImport` both need a `netsuite`\nconnection). Server-required — POST without it fails with 422 \"Expected field:\n_connectionId to be present\" — except for the connection-less flavors (`ToolImport`,\n`AiAgentImport`, `GuardrailImport`), which the server creates without one."},"_integrationId":{"type":["string","null"],"format":"objectId","description":"Integration this import belongs to."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this import was created from, set when the import is part of an installed integration app."},"adaptorType":{"type":"string","description":"Selects the adaptor technology that executes this import, which determines the compatible\nconnection types and which adaptor-specific configuration object must also be supplied\n(e.g. set `salesforce` when using `SalesforceImport`).","enum":["HTTPImport","FTPImport","AS2Import","S3Import","NetSuiteImport","NetSuiteDistributedImport","NetSuiteHTTPImport","SalesforceImport","JDBCImport","RDBMSImport","MongodbImport","DynamodbImport","WrapperImport","AiAgentImport","GuardrailImport","FileSystemImport","ToolImport","RESTImport"]},"nsDomainType":{"type":"string","enum":["suitetalk","restlet"],"description":"Selects which NetSuite REST host a `NetSuiteHTTPImport` 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 each\n`http.relativeURI` entry carries the complete path starting at `/services/rest/...` or\n`/app/site/hosting/restlet.nl`. Ignored on other adaptor types."},"externalId":{"type":["string","null"],"description":"External identifier for correlating the import with a record in another system."},"as2":{"$ref":"#/components/schemas/As2"},"dynamodb":{"$ref":"#/components/schemas/Dynamodb"},"http":{"$ref":"#/components/schemas/Http"},"ftp":{"$ref":"#/components/schemas/Ftp"},"jdbc":{"$ref":"#/components/schemas/Jdbc"},"mongodb":{"$ref":"#/components/schemas/Mongodb"},"netsuite":{"$ref":"#/components/schemas/NetSuite"},"netsuite_da":{"$ref":"#/components/schemas/NetsuiteDistributed"},"rdbms":{"$ref":"#/components/schemas/Rdbms"},"s3":{"$ref":"#/components/schemas/S3"},"wrapper":{"$ref":"#/components/schemas/Wrapper"},"salesforce":{"$ref":"#/components/schemas/Salesforce"},"tool":{"$ref":"#/components/schemas/Tool"},"file":{"$ref":"#/components/schemas/File"},"filesystem":{"$ref":"#/components/schemas/FileSystem"},"aiAgent":{"$ref":"#/components/schemas/AiAgentConfig"},"guardrail":{"$ref":"#/components/schemas/GuardrailConfig"},"name":{"type":"string","minLength":1,"maxLength":100,"description":"Display name for the import, shown in the flow builder, job history, and error logs.\nDescriptive, unique names indicating the destination system and purpose make large\naccounts easier to manage."},"description":{"type":["string","null"],"description":"Free-text summary of what the import writes and why. Shown in the UI and available to\nAI agents for context; has no effect on execution.","maxLength":5120},"unencrypted":{"type":"object","description":"Custom configuration values stored without encryption and returned in API responses."},"sampleData":{"type":["object","array","string"],"description":"Sample input record used to preview and build the import's mappings."},"distributed":{"type":"boolean","description":"When true, the import uses a distributed adaptor (such as `NetSuiteDistributedImport`)\nthat executes inside the target application rather than on Celigo's servers."},"maxAttempts":{"type":"number","description":"Maximum number of attempts made to deliver a record before it is marked as failed."},"ignoreExisting":{"type":"boolean","description":"When true, records that already exist in the destination system are silently skipped\ninstead of being created or updated — used for create-only operations that must avoid\nduplicates. Existing records are identified by the import's lookup configuration or by\na populated `ignoreExtract` field on the incoming record."},"ignoreMissing":{"type":"boolean","description":"When true, records that do not already exist in the destination system are silently\nskipped instead of producing errors — used for update-only operations."},"idLockTemplate":{"type":["string","null"],"description":"Handlebars template that generates a lock key for each record so records resolving to\nthe same key are not submitted concurrently, preventing duplicate or conflicting writes\nto the same target record."},"dataURITemplate":{"type":["string","null"],"description":"Handlebars template that builds a link back to each record in the destination\napplication's UI. The resolved URL is stored with error records in job history so users\ncan jump straight to the record."},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"blobKeyPath":{"type":"string","description":"Path in the input record that holds the blob key identifying the file content to import.\nAt send time the platform follows this path, retrieves the referenced file from integrator.io\nstorage, and streams its bytes into the outgoing request."},"blob":{"type":"boolean","description":"When true, this import transfers raw file content (blobs) to the destination rather than structured records."},"assistant":{"type":"string","description":"Identifier for the connector assistant used to configure this import."},"deleteAfterImport":{"type":"boolean","description":"When true, the source file is deleted after it is successfully imported."},"assistantMetadata":{"type":"object","description":"Metadata associated with the connector assistant configuration."},"useTechAdaptorForm":{"type":"boolean","description":"When true, the UI presents the full technical adaptor form for this import instead of\nthe simplified assistant form."},"distributedAdaptorData":{"type":"object","description":"Internal state stored by distributed adaptors (such as the NetSuite SuiteApp) for this import."},"filter":{"description":"Filter applied to incoming records before they are sent to the destination system.\nRecords that match continue through the import; records that don't are silently\ndropped. Filter expressions reference the incoming record's fields.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"traceKeyTemplate":{"type":"string","description":"Handlebars template that overrides how each record's unique trace key is generated,\nused to track records through the flow and match errors to records. Trace keys are\ncapped at 256 characters; a longer key is stored truncated from the middle, keeping\nthe beginning and end of the value."},"mockResponse":{"type":"array","description":"Predefined response records used in place of calling the destination system when\ntesting the import, so flows can run without writing real data. Records must be in\nintegrator.io canonical format — the server rejects any other shape with a 422.","items":{"type":"object","properties":{"statusCode":{"type":"integer","description":"HTTP status code the mock returns (e.g. 200, 403)."},"id":{"type":["string","integer"],"description":"Identifier echoed for the mocked record."},"ignored":{"type":"boolean","description":"When true, the mocked record reports as ignored rather than imported."},"dataURI":{"type":"string","description":"Data URI echoed for the mocked record."},"errors":{"type":"array","description":"Mock error entries returned with the record."},"_json":{"type":["object","array"],"description":"Mock response body."},"_headers":{"type":"object","description":"Mock response headers."}}}},"_ediProfileId":{"type":"string","format":"objectId","description":"EDI profile this import uses to generate outbound X12 or EDIFACT documents — it supplies\nthe envelope qualifiers, delimiters, version, and validation rules. Set it when the\nimport produces EDI output; omit it otherwise. Accepted on the file-writing imports —\nFTP, AS2, S3 and HTTP file mode (`http.type: \"file\"`)."},"parsers":{"type":"array","description":"Legacy parser configuration slot. The server initializes this field to an empty array\nand current API writes never populate it; treat it as server bookkeeping rather than a\nsetting to configure."},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the import lifecycle for\ntransformations, validation, and custom result handling beyond what configuration alone\ncan express.","properties":{"preMap":{"type":"object","description":"Hook that runs on each page of records before the import's mappings are applied.\nCommonly used to reshape or filter records ahead of mapping.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postMap":{"type":"object","description":"Hook that runs after the import's mappings are applied but before records are sent\nto the destination system. Commonly used for adjustments that need the mapped\n(destination-shaped) record.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postSubmit":{"type":"object","description":"Hook that runs after the destination system responds, with access to both the\nsubmitted records and the destination's responses. Commonly used to inspect results\nor adjust the response data passed downstream.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postAggregate":{"type":"object","description":"Hook that runs after an aggregated file has been submitted to the destination, for\nfile-based imports that combine records into a single file.","allOf":[{"$ref":"#/components/schemas/Hook"}]}}},"sampleResponseData":{"type":["object","array","string"],"description":"Sample response payload used to preview response mappings and test downstream steps\nwithout calling the destination system."},"responseTransform":{"description":"Transformation that reshapes the destination system's response after records are\nimported, before the response is processed by response mappings and downstream\nsteps. Commonly used to extract relevant fields from verbose API responses.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"modelMetadata":{"type":"object","description":"Metadata about the destination data model captured for this import. Rarely set."},"mapping":{"type":"object","description":"Mapper 1.0 mapping configuration. `fields` maps individual fields; `lists` maps\nrecord lists, each with its own `fields` array. Superseded by Mapper 2.0\n(`mappings`) but still widely used.","properties":{"fields":{"type":"array","description":"Field-level mapping entries applied to each record.","items":{"$ref":"#/components/schemas/MappingField"}},"lists":{"type":"array","description":"List-level mappings, each generating a sublist on the target record.","items":{"type":"object","properties":{"generate":{"type":"string","description":"Target sublist or array path to generate."},"fields":{"type":"array","description":"Field mappings applied within each generated list item.","items":{"$ref":"#/components/schemas/MappingField"}}}}}}},"mappings":{"description":"Field mappings that transform incoming records into the destination system's field\nstructure — renaming fields, converting types, building nested objects, and applying\nformulas or lookups to derive values.","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"lookups":{"description":"Top-level mirror of the adaptor-specific ``lookups`` array. Celigo persists lookups\nin TWO locations on every import resource: ``<adaptor_key>.lookups`` (the typed,\nadaptor-specific definition) and this top-level ``lookups`` array (a permissive copy).","allOf":[{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the shape of the input passed to the import's processing pipeline."},"settingsForm":{"$ref":"#/components/schemas/Form"},"preSave":{"$ref":"#/components/schemas/PreSave"},"settings":{"$ref":"#/components/schemas/Settings"}}},"As2":{"type":"object","description":"Configures how outbound AS2 messages are built for this import. Required when the\n_connectionId field references an AS2 connection. AS2 (Applicability Statement 2) transmits\nEDI and other data securely over HTTP/S using S/MIME encryption and digital signatures; this\nobject names the payload file, the message identifier, retry behavior, and any extra headers.","required":["fileNameTemplate"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies the partner-specific EDI and AS2 configuration.\nWhen set, the import inherits the connector's pre-configured settings; omit to use only\nthe AS2 connection details."},"fileNameTemplate":{"type":"string","description":"Handlebars template that names the file carrying each AS2 message payload. Include a\nuniqueness token such as {{timestamp}} so concurrent or repeated sends do not collide;\ndefaults to file-{{timestamp}} in the form."},"messageIdTemplate":{"type":"string","description":"Handlebars template that generates the Message-ID header for each AS2 message. Placeholders\nare replaced at runtime; the resulting value must be globally unique and follow RFC 5322\nheader formatting. Leave unset to let the platform generate the Message-ID."},"maxRetries":{"type":"number","default":0,"description":"Number of times a failed transmission is retried after transient errors such as network\nfailures or timeouts; does not affect the initial send. The form offers 1–5; set to 0\n(the default) to report failure immediately without retrying."},"headers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header sent with the AS2 message (for example, `content-disposition`). Header names are case-insensitive."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions rendered against the record."}}},"description":"HTTP headers sent with the AS2 message transmission.\nSupports both standard AS2 headers and custom headers required by trading partner agreements."}}},"Dynamodb":{"type":"object","description":"Configuration for DynamoDB imports. Required when the _connectionId field references a\nDynamoDB connection; must not be included for other connection types. putItem writes a full\nitem document; updateItem modifies named attributes of an existing item identified by its\nkey.","required":["region","method","tableName"],"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"],"default":"us-east-1","description":"AWS region hosting the DynamoDB table. Determines the service endpoint used for all\nrequests and must match the region where the table is deployed."},"method":{"type":"string","enum":["putItem","updateItem"],"description":"Write operation performed against the DynamoDB table for each record."},"tableName":{"type":"string","description":"DynamoDB table that receives the imported records.\nMust match an existing table in the target AWS account and region; table names are case-sensitive."},"partitionKey":{"type":"string","description":"Value of the target item's partition key for the updateItem operation; together with\nsortKey (when the table defines one) it identifies the item each record writes to.\nSupports handlebars to reference the incoming record's fields."},"sortKey":{"type":"string","description":"Value of the target item's sort key; supports handlebars. Omit when the table defines only\na partition key."},"itemDocument":{"type":"string","description":"JSON document written to the table for the putItem operation, containing all attribute\nnames and values for the item, including its key attributes. Supports handlebars."},"updateExpression":{"type":"string","description":"DynamoDB update expression naming which attributes to set, add, or remove on the item for\nthe updateItem operation. Only the named attributes change; reference attribute names and\nvalues through the expressionAttributeNames and expressionAttributeValues placeholders."},"conditionExpression":{"type":"string","description":"Condition evaluated against the existing item before the write executes.\nWhen the condition evaluates to false, the operation is aborted and no changes are made."},"expressionAttributeNames":{"type":"string","description":"JSON placeholder-to-name map for attribute names referenced in expressions, for example {\"#N\": \"Name\"}.\nUse it when an attribute name is a reserved word or contains special characters; placeholder keys start with #."},"expressionAttributeValues":{"type":"string","description":"JSON placeholder-to-value map for attribute values referenced in expressions; placeholder\nkeys start with a colon (e.g. \":val\") and each value is a DynamoDB attribute-value object\nsuch as {\"S\": \"text\"}. Supports handlebars."},"ignoreExtract":{"type":"string","description":"Path to the field in the source record used to determine whether the record already exists.\nOnly used when ignoreExisting is set on the import."}},"if":{"properties":{"method":{"const":"updateItem"}},"required":["method"]},"then":{"required":["partitionKey","updateExpression"]}},"Http":{"type":"object","description":"Configuration for HTTP imports.\nRequired whenever the connection referenced by _connectionId has type http, and on\n`NetSuiteHTTPImport` (NetSuite's REST APIs on a `netsuite` connection, where `nsDomainType`\nsupplies the host).","properties":{"sendPostMappedData":{"type":"boolean","default":true,"description":"When true, the request body is the record as reshaped by the import's mapping step. When false, the original pre-mapped record is sent instead, bypassing the mappings for the payload."},"isRest":{"type":"boolean","description":"Internal flag indicating the import was built with the legacy REST form rather than the unified HTTP framework. Set by the form; not configured directly."},"strictHandlebarEvaluation":{"type":"boolean","description":"When true, handlebars expressions that reference a missing field raise an error instead of rendering an empty string, surfacing mapping mistakes early rather than sending blank values."},"formType":{"type":"string","enum":["assistant","http","rest","graph_ql","assistant_graphql"],"description":"Determines the UI form and configuration experience for this import."},"type":{"type":"string","enum":["file","records"],"description":"Controls whether the import sends structured record data or raw file content.\nMost HTTP imports use records; use file only when importing raw file content that will be processed downstream."},"requestMediaType":{"type":"string","enum":["xml","json","csv","urlencoded","form-data","octet-stream","plaintext"],"description":"Content type used to serialize the request body sent to the target API.\nMost REST APIs use json."},"_httpConnectorEndpointIds":{"type":"array","readOnly":true,"items":{"type":"string","format":"objectId"},"description":"HTTP connector endpoint IDs used by this import (multiple endpoints for different request\ntypes or operations). Set by the connector framework; client-supplied values are ignored."},"blobFormat":{"type":"string","enum":["utf8","ucs2","utf-16le","ascii","binary","base64","hex"],"description":"Character encoding format for blob/binary data imports.\nOnly relevant when type is \"file\" or when handling binary content.\n"},"batchSize":{"type":"integer","description":"Maximum number of records submitted per HTTP request, which affects throughput and API rate limiting.\nREST services typically send one record per request; raise it only for batch endpoints or RPC/XML services that accept multiple records, consulting the target API documentation for the optimal value.\n\nThe default varies by API."},"successMediaType":{"type":"string","enum":["xml","json","plaintext"],"description":"Media type expected in successful responses from the target API. Most APIs return json."},"requestType":{"type":"array","items":{"type":"string","enum":["CREATE","UPDATE"]},"description":"Operation type each request performs; for composite (upsert) imports, include both values.\nThe array is positionally aligned with the relativeURI and method arrays — each index maps to one operation — and the existingExtract field determines which operation runs at runtime."},"errorMediaType":{"type":"string","enum":["xml","json","plaintext"],"description":"Media type expected in error responses from the target API. Most APIs return json."},"followRedirects":{"type":"boolean","default":true,"description":"When explicitly false, 3xx responses are not followed — the redirect\nresponse itself (status code, `Location` header, body) is what the\nimport records as the result. Omitted or true follows allowed\nredirects (the default behavior)."},"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":"array","items":{"type":"string"},"description":"Relative URI path for each import request, as an array of strings (never an object); values can include Handlebars expressions for dynamic segments.\nTemplates render against the pre-mapped record — the original input before the Import's mapping step — because URI construction usually needs business identifiers that mappings may rename or remove.\nFor composite (upsert) imports, the array is positionally aligned with method and requestType, and existingExtract decides the index used at runtime: the UPDATE index when its field has a value, the CREATE index when it is empty or missing.\nReference the existing record ID in the UPDATE URI with `{{{data.0.fieldName}}}` (triple braces with the data.0 prefix).\nUse separate array elements per operation rather than `{{#if}}` conditionals inside a single URI."},"method":{"type":"array","items":{"type":"string","enum":["GET","PUT","POST","PATCH","DELETE"]},"description":"HTTP method used for each import request.\nFor composite (upsert) imports, the array is positionally aligned with relativeURI and requestType — e.g. `[\"PUT\", \"POST\"]` with `requestType: [\"UPDATE\", \"CREATE\"]` uses PUT for updates and POST for creates."},"_httpConnectorVersionId":{"type":"string","format":"objectId","readOnly":true,"description":"HTTP connector version used by this import. Set by the connector framework; client-supplied values are ignored (write-tested)."},"_httpConnectorResourceId":{"type":"string","format":"objectId","readOnly":true,"description":"HTTP connector resource used by this import. Set by the connector framework; client-supplied values are ignored (write-tested)."},"_httpConnectorEndpointId":{"type":"string","format":"objectId","readOnly":true,"description":"HTTP connector endpoint used by this import (single endpoint). Set by the connector framework; client-supplied values are ignored (write-tested)."},"body":{"type":"array","items":{"type":["string","null"]},"description":"Request body template for the import — an array of Handlebars template strings\npositionally aligned with the `method` array (e.g. `[\"{{{record}}}\"]`). A `null`\nentry means no template for the method at that position. Each entry is a string:\nwrite a JSON body as a template string (`[\"{\\\"id\\\": \\\"{{record.id}}\\\"}\"]`), not as\na JSON object, because an object entry does not render as JSON at runtime.\nLeave undefined for standard imports — the Import's mapping step transforms the source record and the mapped data is sent as the request body automatically.\nSet it only when the target API requires XML, SOAP, or a custom structure that mappings cannot produce, or when the user explicitly specifies a request body.\nWhen set, the mapping step still runs first and the template renders against the post-mapped record — use `{{record.<mappedField>}}` to reference mapping outputs.\nHand-templated bodies are harder to maintain and debug than mappings; when in doubt, leave this field undefined.\n\nCustom envelopes can combine static header or account fields with per-message\nfield references inside a loop.\n\nStatic wrappers around the mapped record are appropriate for message-bus class envelopes,\nHMAC signature wrappers, integration batch headers,\nnamed single-record array wrappers, and similar destination-required structures.\n\nBatch templates can wrap an envelope, such as a header, version, or marketplace,\naround serialized records.\n\nUse this pattern when each message needs a small number of fields rather than the whole record."},"ignoreEmptyNodes":{"type":"boolean","description":"When true, fields with empty values are stripped from the request body before it is sent,\nso the target API receives only populated fields. Shown as \"Remove empty fields from HTTP\nrequest body\" in the UI."},"existingExtract":{"type":"string","description":"Field name or JSON path that drives the upsert decision for composite imports with both CREATE and UPDATE in requestType.\nWhen the field has a value in the incoming record, the UPDATE operation's relativeURI and method are used; when it is empty or missing, the CREATE operation runs.\nMust match a field populated by an upstream lookup or response mapping (e.g. a destination system ID like \"shopifyCustomerId\"); omit for single-operation imports."},"existingLookupName":{"type":"string","description":"Name of an entry in `lookups` that resolves whether each record already exists in the destination, driving the composite upsert decision when `existingExtract` alone is insufficient (e.g. the check requires a call to the target system). Paired with composite CREATE/UPDATE imports.\n\nUse `existingLookupName` when the incoming record has no destination ID and\nneeds a per-record probe to discover one;\nuse `existingExtract` when the record already carries the ID.\n\nFor single-operation imports,\nuse `ignoreExtract` or `ignoreLookupName` with `ignoreExisting` or\n`ignoreMissing` to skip records instead of routing them.\n\nThe lookup pattern adds one HTTP request per record at runtime,\nmaking it better suited to one-off migrations and low-volume reconciliation than\nhigh-volume recurring syncs."},"ignoreExtract":{"type":"string","description":"Field name on the incoming record that drives the SKIP-vs-PROCESS\ndecision when ``ignoreExisting`` or ``ignoreMissing`` is set.\n\nPairs with either of the two skip-mode flags:\n\n- With ``ignoreExisting: true`` (CREATE-only imports that should\n  avoid duplicates): if ``ignoreExtract`` has a value on the\n  record, the record is treated as already-existing and is\n  skipped; if empty, the record is created.\n- With ``ignoreMissing: true`` (UPDATE-only imports that should\n  avoid creating new records): if ``ignoreExtract`` has a value,\n  the record is treated as existing and is updated; if empty,\n  the record is skipped.\n\n``ignoreExtract`` checks a field that's already on the record —\nzero per-record HTTP overhead.  Use ``ignoreLookupName`` instead\nwhen the record has no field to inspect and the existence check\nneeds a per-record probe against the destination.\n\n**Mutually exclusive with ``ignoreLookupName``**\n\nSet EXACTLY ONE of ``ignoreExtract`` / ``ignoreLookupName`` when\n``ignoreExisting`` or ``ignoreMissing`` is enabled.\n\n**When not to set this field**\n\nOnly valid for single-operation imports with either\n``ignoreExisting: true`` or ``ignoreMissing: true`` set.\nComposite (upsert) imports — those with both ``CREATE`` and\n``UPDATE`` in ``requestType`` — use ``existingExtract`` /\n``existingLookupName`` instead, because they route records\nbetween two write paths rather than skipping them."},"endPointBodyLimit":{"type":"integer","description":"Maximum size limit for the request body in bytes.\nUsed to enforce API-specific size constraints.\n"},"headers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header to include with each import request."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions rendered against the pre-mapped record."}}},"description":"Custom HTTP headers included with import requests; values can contain Handlebars expressions.\nValue templates render against the pre-mapped record (the original input before the Import's mapping step) — use `{{record.<field>}}` to reference fields as they appear in the upstream source."},"response":{"type":"object","properties":{"resourcePath":{"type":"array","items":{"type":"string"},"description":"Per-operation JSON path to the resource collection in each\nresponse.  Outer-array length MUST match ``requestType``\nlength: 1 entry for single-op imports, N entries for an\nN-operation composite import (duplicate the value when\noperations agree).  See the ``response`` object docs\nabove for the full positional-array contract.\n\nRequired when ``batchSize > 1`` because the platform needs\nto know where the array of result records lives inside the\nresponse envelope.  When the API returns a bare array at\nthe top level, leave this field unset.\n\nEach entry is a DOT-PATH STRING (e.g. ``\"data.results\"``,\n``\"items\"``) — do NOT split paths into segments across\narray elements."},"resourceIdPath":{"type":"array","items":{"type":["string","null"]},"description":"Per-operation JSON path to the unique ID field within each\nrecord in the response.  Outer-array length MUST match\n``requestType`` length — duplicate the value when both\noperations agree (the common case) rather than collapsing\nto a 1-entry array.\n\nEach entry is a DOT-PATH STRING, or null for an operation\nwith no ID path configured. When not specified, the platform\nlooks for standard `id` or `_id` fields automatically."},"successPath":{"type":"array","items":{"type":"string"},"description":"Per-operation JSON path to a field that indicates whether\nthe API call succeeded.  Outer-array length MUST match\n``requestType`` length — duplicate the value when both\noperations agree rather than collapsing to a 1-entry\narray on a composite import.\n\nUse this when the API returns HTTP 200 for everything and\nsignals success / failure through a body field.  Pairs with\n``successValues`` to define which values at this path mean\nsuccess.\n\nEach entry is a DOT-PATH STRING."},"successValues":{"type":"array","items":{"type":"array","items":{"type":"string"}},"description":"Per-operation list of values at ``successPath`` that mean\nsuccess.  TWO levels of array nesting:\n\n- Outer array: one entry per ``requestType`` index.\n  Length MUST match ``requestType`` length — duplicate\n  the inner array when both operations agree rather than\n  collapsing to a 1-entry outer array on a composite\n  import.\n- Inner array: the list of acceptable success values for\n  that operation.\n\nAll comparisons are STRING comparisons regardless of the\nactual response value type — the platform stringifies\nnumbers, booleans, etc. before comparing."},"failPath":{"type":"array","items":{"type":"string"},"description":"Per-operation JSON path to a field that signals failure\neven when HTTP returns 200.  Outer-array length MUST\nmatch ``requestType`` length — duplicate when both\noperations agree.  Same array semantics as ``successPath``.\n\nSome APIs use only ``successPath`` (failure = absence of\nsuccess); others publish both an explicit success indicator\nand an explicit failure indicator.  When both are\navailable, set both — the platform evaluates ``successPath``\nfirst and falls through to ``failPath`` only when the\nsuccess check is inconclusive."},"failValues":{"type":"array","items":{"type":"array","items":{"type":"string"}},"description":"Per-operation list of values at ``failPath`` that mean\nfailure.  Same two-level array shape and length contract\nas ``successValues`` — outer length MUST match\n``requestType`` length, duplicate the inner array on\ncomposite imports when both operations agree.\n\nOften used as the inverse of ``successValues`` (e.g.\n``successValues: [[\"true\"]]`` paired with\n``failValues: [[\"false\"]]``)."},"errorPath":{"type":"string","description":"JSON path to the error message inside the response body.\n\n**Note**: this field is NOT a per-operation array — it's a\nsingle string applied to every operation.  Most APIs use\nthe same error-message field shape for both UPDATE and\nCREATE responses, so the platform doesn't expose a per-op\noverride here."},"allowArrayforSuccessPath":{"type":"boolean","description":"When true, allows array values at successPath during success evaluation."},"hasHeader":{"type":"boolean","description":"When true, treats the first record in the response as a header row (for CSV responses)."}},"description":"Configuration for parsing and interpreting HTTP responses returned\nfrom each write request the import issues.\n\n**Critical: positional per-operation arrays**\n\nEvery field inside ``response`` (``resourcePath``, ``resourceIdPath``,\n``successPath``, ``successValues``, ``failPath``, ``failValues``)\nis an ARRAY whose outer length is positionally aligned with\n``requestType`` / ``method`` / ``relativeURI``.  This is the\nSAME positional-alignment contract as those three sibling\narrays — see ``relativeURI`` docs for the full explanation.\n\nEach outer-array index describes how to parse the response from\nthe operation at that same index in ``requestType``:\n\n- **Single-operation import** (``requestType: [\"CREATE\"]``):\n  every response field is a 1-element array.\n  ``successPath: [\"ok\"]`` means \"for the one operation,\n  look at path ``ok`` to detect success\".\n- **Composite upsert** (``requestType: [\"UPDATE\", \"CREATE\"]``):\n  every response field is a 2-element array.  Index 0 parses\n  the UPDATE response, index 1 parses the CREATE response.\n\n**successValues / failValues — array of arrays**\n\n``successValues`` and ``failValues`` carry an extra array level\nbecause each operation can have MULTIPLE acceptable values:\n\n- Outer array: one entry per operation (positional, as above).\n- Inner array: the list of acceptable values for that operation.\n\nExample:\n```json\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"successPath\": [\"ok\", \"ok\"],\n\"successValues\": [[\"true\", \"1\"], [\"true\"]]\n```\nMeans: the UPDATE response is successful when ``ok`` is either\n``\"true\"`` or ``\"1\"``; the CREATE response is successful only\nwhen ``ok`` is ``\"true\"``.\n\n**Always duplicate when operations agree (REQUIRED for composite)**\n\nThe outer-array length MUST match ``requestType`` length on\nevery response field.  When the UPDATE and CREATE responses\nof the same connector look the same (which is the common\ncase — most APIs return the same shape from both endpoints),\nDUPLICATE the entry so the array has the same length as\n``requestType``.  Do NOT collapse to a 1-entry array on a\ncomposite import.\n\n```json\n// ✅ Correct — explicit per-operation entries, even when identical\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data.id\", \"data.id\"],\n\"successPath\": [\"success\", \"success\"],\n\"successValues\": [[\"true\"], [\"true\"]]\n```\n\n```json\n// ❌ Wrong — 1-entry array on a composite import\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data.id\"],\n\"successPath\": [\"success\"],\n\"successValues\": [[\"true\"]]\n```\n\nWhy duplicate even when they agree:\n\n- **UI parity.**  The Celigo UI renders one response-handling\n  form per ``requestType`` index.  A 1-entry array on a\n  composite import leaves the second operation's form panel\n  empty, which looks like a half-built config to the user\n  reviewing the saved import — even if it works at runtime.\n- **Symmetric shape rule.**  ``relativeURI`` / ``method`` /\n  ``requestType`` REQUIRE the entry count to match across\n  all three (the runtime decider uses positional alignment).\n  Making ``response.*`` follow the same \"outer length ==\n  requestType length\" rule means there's ONE shape rule to\n  remember, not two.\n- **Unambiguous intent.**  A 2-entry duplicated array\n  documents in the saved config that the developer\n  considered both operations.  A 1-entry array could mean\n  \"both ops use this\" or could mean \"I forgot to fill in\n  the second op.\"  Duplication eliminates the ambiguity.\n\nWhen operations DIFFER (rare — different response shapes\nper endpoint), set distinct values at each index.  Either\nway the outer length always matches ``requestType``.\n\n**Common llm mistake (DO not do THIS)**\n\nDo NOT split a single dot-path into array segments thinking the\nouter array represents path segments:\n\n```json\n// WRONG — the LLM expressed \"data.id\" as path segments\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data\", \"id\"]\n```\n\nThis is interpreted by the platform as: UPDATE response's id is\nat path ``data``, CREATE response's id is at path ``id`` — almost\nnever what the user wants.  The CORRECT shape is the single\ndot-path expressed as ONE string per operation, duplicated to\nmatch ``requestType`` length:\n\n```json\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data.id\", \"data.id\"]\n```"},"_asyncHelperId":{"type":"string","format":"objectId","description":"Reference to an AsyncHelper resource that polls an asynchronous\ndestination API on this import's behalf.\n\nSet this ONLY when the destination API is genuinely asynchronous — it\nacknowledges the submitted payload (HTTP 202, a job ticket) and ingests\nit in the background, so completion must be polled for (e.g. bulk\nimage/file ingestion, large bulk-load endpoints). Most imports are\nsynchronous and need NO async helper; adding one to a synchronous\ndestination just adds polling overhead plus a status and result export\nto maintain. When in doubt, leave it unset.\n\nThe referenced helper bundles the polling config plus a required status\nexport (polled to check progress) and a result export (fetches the\nfinal payload). An import configured with an async helper cannot carry\nits own transform, output filter, or preSavePage hook — put that\nprocessing in the result export instead."},"ignoreLookupName":{"type":"string","description":"Name of an entry in `lookups` used to check whether each record already exists, so `ignoreMissing`/`ignoreExisting` can skip it. Use it instead of `ignoreExtract` when existence must be resolved by a call to the target system rather than read from a field on the incoming record."},"lookups":{"description":"Lookups referenced by `existingLookupName`/`ignoreLookupName` to resolve record existence against the destination system at runtime.","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia ``lookupName`` and Handlebars expressions reference it via\n``{{lookup.name}}``. Must be unique within this\nresource's ``lookups`` array.\n"},"method":{"type":"string","enum":["GET","POST","PUT","PATCH","DELETE"],"description":"HTTP method used to issue the lookup request. ``GET`` and\n``POST`` are most common. Omit when using a static ``map`` or\n``_lookupCacheId``.\n"},"relativeURI":{"type":"string","description":"HTTP path (relative to the connection's base URL) that\nresolves the lookup. Use Handlebars with triple braces to\ninject values from the incoming record — e.g.\n``/customers?email={{{email}}}`` or\n``/accounts/{{{accountId}}}``.\n"},"postBody":{"type":"string","description":"Request body template for POST / PUT / PATCH lookup methods.\nTypically a JSON string with Handlebars placeholders —\n``{\"email\":\"{{{email}}}\"}``. Ignored for GET / DELETE.\n"},"headers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}},"description":"Optional custom headers to attach to the lookup request.\nUseful for endpoints that need a different Accept header or\nan auxiliary auth token separate from the connection-level\nheaders.\n"},"extract":{"type":"string","description":"JSONPath expression that selects the lookup value from the\nHTTP response body. ``$.`` prefix optional. Examples:\n``$.data[0].id``, ``$.results.primaryKey``, ``id``.\n"},"_lookupCacheId":{"type":"string","format":"objectId","description":"Optional reference to a LookupCache resource. When set, the\nlookup reads from the cache instead of issuing a live HTTP\nrequest — useful for stable reference data that should be\npre-loaded.\n"},"map":{"type":["object","null"],"description":"Optional static key→value object evaluated BEFORE the HTTP\nrequest. When the incoming value matches a key, the mapped\nvalue is returned without hitting the remote endpoint. Null\non dynamic lookups with no static map.\n"},"default":{"type":["string","null"],"description":"Value returned when the HTTP request returns no usable\nmatch. When omitted and ``allowFailures`` is false, an\nunmatched lookup halts the record. Stored as null when no\nfallback is configured.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, a lookup miss (no matching response and no\n``default``) resolves to ``null`` and the import continues.\nWhen false (default), a miss halts the record. May be\nstored as null (treated as unset).\n"},"useDefaultOnMultipleMatches":{"type":"boolean","description":"Controls behaviour when the response contains more than one\nmatch. When true, the ``default`` value is returned instead\nof raising an ambiguous-match error. When false (default),\nmultiple matches are treated as a failure.\n"}}},"type":"array"}}},"Ftp":{"type":"object","description":"Defines where files are written on 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 target folder and the file naming comes from\nthe import's file configuration.","required":["directoryPath"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies partner-specific B2B settings for this import.\nWhen set, the import inherits the connector's pre-configured settings; omit to use only\nthe FTP connection details."},"directoryPath":{"type":"string","description":"Directory on the server where imported files are written, either absolute or relative to\nthe login directory; the FTP user must have write permission on it. Use forward slashes\nregardless of server OS — paths are case-sensitive on UNIX/Linux servers. Supports\nhandlebars templates."},"fileName":{"type":"string","description":"Name of the file written to the server, including its extension; do not include directory\nseparators — the location comes from directoryPath. Supports handlebars placeholders such\nas items-{{timestamp}}.csv to generate a unique name per run."},"inProgressFileName":{"type":"string","description":"Temporary name the file carries while its upload is in progress, preventing other systems\nfrom processing a partially transferred file; it is renamed to its final name once the\nupload completes. Include a handlebars uniqueness token (e.g. {{timestamp}}) when one file\nis written per flow run."},"backupDirectoryPath":{"type":"string","description":"Directory on the same server where a copy of each written file is retained after a\nsuccessful import; if omitted, no server-side backup is kept. Supports static paths or\nhandlebars templates."}}},"Jdbc":{"type":"object","description":"Configuration for JDBC import operations. Defines how data is written to a database\nvia a JDBC connection.\n\n**Query type determines which fields are required**\n\n| queryType        | Required fields              | Do NOT set        |\n|------------------|------------------------------|-------------------|\n| [\"per_record\"]   | query (array of SQL strings) | bulkInsert        |\n| [\"per_page\"]     | query (array of SQL strings) | bulkInsert        |\n| [\"bulk_insert\"]  | bulkInsert object            | query             |\n| [\"bulk_load\"]    | bulkLoad object              | query             |\n\n**Critical:** query IS AN ARRAY OF STRINGS\nThe query field must be an array of plain strings, NOT a single string and NOT an array of objects.\nCorrect: [\"INSERT INTO users (name) VALUES ('{{{name}}}')\"]\nWrong: \"INSERT INTO users ...\"\nWrong: [{\"query\": \"INSERT INTO users ...\"}]","required":["queryType"],"properties":{"query":{"type":"array","items":{"type":"string"},"description":"Array of SQL query strings to execute. REQUIRED when queryType is [\"per_record\"] or [\"per_page\"].\n\nEach element is a complete SQL statement as a plain string. Typically contains a single query.\n\nUse Handlebars {{fieldName}} syntax to inject values from incoming records.\n\n**Format — array of strings**\n- CORRECT: [\"INSERT INTO users (name, email) VALUES ('{{{name}}}', '{{{email}}}')\"]\n- WRONG: \"INSERT INTO users ...\"  (not an array)\n- WRONG: [{\"query\": \"INSERT INTO users ...\"}]  (objects are invalid — causes Cast error)\n\n**Examples**\n- INSERT: [\"INSERT INTO users (name, email) VALUES ('{{{name}}}', '{{{email}}}')\"]\n- UPDATE: [\"UPDATE inventory SET qty = {{{quantity}}} WHERE sku = '{{{sku}}}'\"]\n- MERGE/UPSERT: [\"MERGE INTO target USING (SELECT CAST(? AS VARCHAR) AS email) AS src ON target.email = src.email WHEN MATCHED THEN UPDATE SET name = ? WHEN NOT MATCHED THEN INSERT (name, email) VALUES (?, ?)\"]\n\n**String vs numeric values in handlebars**\n- Use triple braces so the raw value is emitted; double braces emit the value already single-quoted, so wrapping them in quotes double-quotes it.\n- Strings: supply the quotes yourself — '{{{name}}}'\n- Numbers: no quotes — {{{quantity}}}"},"queryType":{"type":"array","items":{"type":"string","enum":["bulk_insert","per_record","per_page","bulk_load"]},"description":"Execution strategy for the SQL operation. REQUIRED. Must be an array with one value.\n\n**Decision tree**\n\n1. If UPDATE or UPSERT/MERGE → [\"per_record\"] (set query field)\n2. If INSERT with \"ignore existing\" / \"skip duplicates\" / match logic → [\"per_record\"] (set query field)\n3. If pure INSERT with no duplicate checking → [\"bulk_insert\"] (set bulkInsert object)\n4. If high-volume bulk load → [\"bulk_load\"] (set bulkLoad object)\n\n**Critical relationship to other fields**\n| queryType        | REQUIRES              | DO NOT SET   |\n|------------------|-----------------------|--------------|\n| [\"per_record\"]   | query (array)         | bulkInsert   |\n| [\"per_page\"]     | query (array)         | bulkInsert   |\n| [\"bulk_insert\"]  | bulkInsert object     | query        |\n| [\"bulk_load\"]    | bulkLoad object       | query        |\n\n**Examples**\n- Per-record upsert: [\"per_record\"]\n- Bulk insert: [\"bulk_insert\"]\n- Bulk load: [\"bulk_load\"]\n"},"bulkInsert":{"type":"object","description":"Bulk insert configuration. REQUIRED when queryType is [\"bulk_insert\"]. DO NOT SET when queryType is [\"per_record\"].\n\nEnables efficient batch insertion of records into a database table without per-record SQL.\n","properties":{"tableName":{"type":"string","description":"Target database table name for bulk insert. REQUIRED.\nCan include schema qualifiers (e.g., \"schema.tableName\").\n"},"batchSize":{"type":"string","description":"Number of records per batch during bulk insert.\nLarger values improve throughput but use more memory.\nCommon values: \"1000\", \"5000\".\n"}}},"bulkLoad":{"type":"object","description":"Bulk load configuration. REQUIRED when queryType is [\"bulk_load\"]. Uses database-native bulk loading for maximum throughput.\n","properties":{"tableName":{"type":"string","description":"Target database table name for bulk load. REQUIRED.\nCan include schema qualifiers (e.g., \"schema.tableName\").\n"},"primaryKeys":{"type":["array","null"],"items":{"type":"string"},"description":"Primary key column names for upsert/merge during bulk load.\nWhen set, existing rows matching these keys are updated; non-matching rows are inserted.\nExample: [\"id\"] or [\"order_id\", \"product_id\"] for composite keys.\n"},"overrideMergeOrInsertQuery":{"type":"boolean","description":"When true, a custom merge/insert query replaces the auto-generated statement for the\nbulk load, enabling ignore-existing logic or conditional updates."}}},"lookups":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia `lookupName` and Handlebars expressions reference it via\n`{{lookup \"name\" value}}`. Must be unique within this\nresource's `lookups` array."},"query":{"type":"string","description":"SQL query for the lookup (e.g., \"SELECT id FROM users WHERE email = '{{{email}}}'\")."},"extract":{"type":"string","description":"JSONPath-style expression that selects the value from the\nquery result row (e.g. `id`, `details.price`). Omit when\nthe query returns a single scalar."},"_lookupCacheId":{"type":"string","format":"objectId","description":"Optional reference to a LookupCache resource. When set, the\nlookup reads from the cache instead of issuing a live SQL\nquery — useful for stable reference data that should be\npre-loaded."},"allowFailures":{"type":"boolean","description":"When true, a lookup miss (no matching row and no `default`)\nresolves to `null` and the import continues. When false\n(default), a miss halts the record."},"map":{"type":"object","description":"Optional static key→value object evaluated BEFORE the SQL\nquery. When the incoming value matches a key, the mapped\nvalue is returned without querying the database."},"default":{"type":"string","description":"Value returned when the query matches no rows. When omitted\nand `allowFailures` is false, an unmatched lookup halts the\nrecord."}}},"description":"Lookup definitions executed against the JDBC connection. Each entry\nruns a `SELECT` and extracts a value used by field mappings\n(referenced via `lookupName`) or Handlebars expressions\n(referenced via `{{lookup \"name\" value}}`)."}}},"Mongodb":{"type":"object","description":"Configuration for MongoDB imports. Contains only the MongoDB-specific properties; import-level fields such as ignoreExisting, ignoreMissing, name, and description are set on the parent import, not here.","required":["method","collection"],"properties":{"method":{"type":"string","enum":["insertMany","updateOne"],"description":"Write operation performed against the MongoDB collection for each record."},"collection":{"type":"string","description":"MongoDB collection that receives the imported records.\nCollection names are case-sensitive and must not start with the reserved \"system.\" prefix."},"filter":{"type":"string","description":"MongoDB query filter that selects the document each updateOne applies to; required when\nmethod is updateOne. Supports standard MongoDB query operators and dot notation, and\nhandlebars placeholders to reference the incoming record's fields."},"document":{"type":"string","description":"Content of the document written to the collection (used when method is insertMany), as a JSON string that can include nested documents and arrays and handlebars placeholders."},"update":{"type":"string","description":"Modifications applied to matching documents (used when method is updateOne), using MongoDB update operators such as $set, $inc, or $push.\nA complete document without operators replaces the matched document entirely."},"upsert":{"type":"boolean","description":"When true, inserts a new document built from the update criteria if no existing document matches the filter.\nWhen false, records with no matching document are skipped and nothing is inserted."},"ignoreExtract":{"type":"string","description":"Path to the field in the source record that identifies existing records. When that field has a value, the record is treated as existing and skipped.\nOnly used when the import-level ignoreExisting flag is true; do not set it otherwise.\nEnclose field names containing special characters in square brackets (for example, [vendor-code]), and reference array items with an index, such as items[0].id."},"ignoreLookupFilter":{"type":"string","description":"JSON-stringified MongoDB query filter used to find existing documents in the target collection when the import-level ignoreExisting flag is true.\nIf the query matches a document, the incoming record is treated as existing and skipped.\nUse Handlebars placeholders to reference values from the incoming record, for example \"{\\\"email\\\":\\\"{{email}}\\\"}\".\nUnlike ignoreExtract, which only checks whether a field on the incoming record has a value, this filter queries the MongoDB collection itself."}},"if":{"properties":{"method":{"const":"updateOne"}},"required":["method"]},"then":{"required":["filter"]}},"NetSuite":{"type":"object","description":"Configuration for NetSuite imports (legacy adaptor; use netsuite_da / NetSuiteDistributedImport for SuiteApp 2.0).","properties":{"lookups":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique name for this lookup, referenced from mappings and handlebars templates with `{{lookup 'name' value}}`."},"recordType":{"type":"string","description":"NetSuite record type searched by a dynamic lookup (for example, `customer`, `salesOrder`). Set with `searchField` or `expression`; omit for a static `map` lookup."},"searchField":{"type":"string","description":"NetSuite field the dynamic lookup searches on (for example, `externalid`). Must be a searchable field on `recordType`. Use this or `expression`."},"expression":{"type":"string","description":"NetSuite search expression evaluated at runtime to select matching records (for example, `[\"recipient\",\"is\",\"123\"]`). Use this or `searchField`."},"resultField":{"type":"string","description":"Field on the matched NetSuite record whose value the lookup returns (for example, `internalid`).\nSupports dot notation for nested fields."},"map":{"type":["object","null"],"description":"Static lookup table with input values as keys and their corresponding output values. Use instead of a NetSuite search for fixed value translations. The platform stores `null` here on search-driven lookups."},"default":{"type":["string","null"],"description":"Fallback value returned when the lookup finds no match. Used together with `allowFailures`. Stored as `null` when not configured."},"allowFailures":{"type":"boolean","description":"When true, an unmatched lookup falls back to `default` and processing continues; when false, an unmatched lookup fails the record."},"_id":{"type":"object","description":"Server-assigned unique identifier for this lookup entry.","readOnly":true}}},"description":"Lookup definitions used to resolve reference values during the import, using either a static `map` or a dynamic NetSuite record search."},"operation":{"type":"string","enum":["add","update","addupdate","attach","detach","delete"],"description":"Operation to perform on NetSuite records, as a plain string (the API lowercases the value).\nFor `update`, `addupdate`, and `delete`, also set internalIdLookup so existing records can be found; for `add` with ignoreExisting, set internalIdLookup to check for duplicates before creating.\nDefault to `addupdate` when the intent is \"sync\", \"upsert\", or \"create or update\"; use `add` for plain \"create\" or \"insert\".\nDo not wrap the value in an object like `{\"type\": \"addupdate\"}` — that fails validation."},"isFileProvider":{"type":"boolean","description":"When true, enables file operations against NetSuite — browsing, uploading, downloading, updating, and deleting files.\nWhen false or omitted, file-related operations are unavailable through this import."},"customFieldMetadata":{"type":"object","description":"Metadata describing the custom fields defined on the target NetSuite record type, used to validate and process custom field data during the import."},"recordType":{"type":"string","description":"NetSuite record type this import writes to (e.g. customer, salesOrder, invoice).\nUse the exact NetSuite internal record type identifier; both standard and custom record types are supported."},"recordTypeId":{"type":"string","description":"The unique identifier specifying the record type within NetSuite. Determines the applicable schema, fields, validation rules, and processing logic. Use exact NetSuite record type identifiers such as standard types or custom record IDs."},"retryUpdateAsAdd":{"type":"boolean","description":"When true, an update that fails because the target record does not exist is automatically retried as an add.\nWhen false or unset, update failures error immediately without retrying.\nMay create duplicate records if the update failed for a reason other than a missing record."},"batchSize":{"type":"number","description":"Number of records sent to NetSuite per API call.\nLarger batches reduce the number of calls but increase memory use and timeout risk per call."},"internalIdLookup":{"type":"object","properties":{"extract":{"type":"string","description":"Path used to extract the value for the internal ID lookup."},"searchField":{"type":"string","description":"The NetSuite record field used as the key attribute for the internal ID lookup search. Should be a unique or indexed field to ensure accurate matches. Must correspond to a valid, searchable field on the target record type."},"expression":{"type":"string","description":"NetSuite search expression that filters which records match the lookup.\nSupports logical operators (AND, OR, NOT), comparison operators, and nested conditions, evaluated at runtime against current data."}},"description":"Configuration for locating existing NetSuite records by internal ID.\nRequired when operation is `update`, `addupdate`, or `delete`; also used with `add` plus ignoreExisting to check for duplicates."},"preferences":{"type":"object","properties":{"ignoreReadOnlyFields":{"type":"boolean","description":"When true, silently skips read-only fields during update operations instead of raising errors."},"warningAsError":{"type":"boolean","description":"When true, treats NetSuite warnings as errors, causing the operation to fail immediately."},"skipCustomMetadataRequests":{"type":"boolean","description":"When true, skips custom metadata requests during NetSuite API operations to improve performance."}},"description":"Preferences that control how NetSuite handles the import operation."},"file":{"type":"object","properties":{"name":{"type":"string","description":"Filename for the file in the NetSuite File Cabinet, without any folder or path information.\nMust be unique within its folder; include the file extension (e.g. .pdf, .csv)."},"fileType":{"type":"string","description":"NetSuite file type that governs how the file is processed, stored, and displayed.\nMust match the actual content format of the file."},"folder":{"type":"string","description":"Folder in the NetSuite File Cabinet where the file is stored, as a numeric folder ID or a folder path.\nUpdating this on an existing file moves the file to the specified folder."},"folderInternalId":{"type":"string","description":"Internal ID of the File Cabinet folder where the file is stored.\nMust reference an existing folder; changing it moves the file to a different folder."},"internalId":{"type":"string","description":"NetSuite internal ID of an existing file, used to target it for retrieval, updates, or deletion.\nAssigned by NetSuite when the file is created and cannot be changed."},"backupFolderInternalId":{"type":"string","description":"Internal ID of the File Cabinet folder where backup files are stored."}},"description":"Configuration for the file to upload to or update in the NetSuite File Cabinet."}},"if":{"not":{"propertyNames":{"enum":["preferences","lookups"]}}},"then":{"required":["operation"],"if":{"properties":{"isFileProvider":{"const":true}},"required":["isFileProvider"]},"else":{"required":["recordType"]}}},"NetsuiteDistributed":{"type":"object","description":"Configuration for NetSuite Distributed (SuiteApp 2.0) import operations — the primary sub-schema for the NetSuiteDistributedImport adaptorType. The API field name is \"netsuite_da\".\noperation and recordType are required. The server may attach empty bookkeeping stubs of\nthis object (empty lookups/mapping, or missingOrCorruptedDAConfig) to imports of other\nadaptor types; stubs omit these fields. Set internalIdLookup so existing records\ncan be found when operation is update, addupdate, or delete.","properties":{"operation":{"type":"string","enum":["add","update","addupdate","attach","detach","delete"],"description":"Operation to perform on the target NetSuite record, as a plain string. Required.\nFor `update`, `addupdate`, and `delete`, also set internalIdLookup so existing records can be found."},"recordType":{"type":"string","description":"NetSuite record type to import into (e.g. \"customer\", \"salesorder\", \"customrecord_myrecord\" for custom record types). Required.\nMust match a valid NetSuite record type identifier."},"recordIdentifier":{"type":"string","description":"Custom record identifier, used to identify the specific record type when\nimporting into custom record types.\n"},"restletVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"Version of the NetSuite RESTlet to use, as a plain string.\nDefaults to \"suiteapp2.0\" when useSS2Restlets is true, \"suitebundle\" otherwise; rarely needs to be set explicitly for modern integrations.\nThe version is fixed when the step is created — the Advanced selector is disabled on existing steps, and migrating an existing step to a different version means recreating or cloning it."},"useSS2Restlets":{"type":"boolean","description":"When true, uses SuiteScript 2.0 RESTlets and restletVersion defaults to \"suiteapp2.0\".\nDefaults to true for modern integrations.\nSet at step creation together with restletVersion — the same creation-time-only constraint applies (see restletVersion)."},"missingOrCorruptedDAConfig":{"type":"boolean","description":"When true, the Distributed Adaptor configuration is missing or corrupted.\nSet by the system — do not set manually."},"batchSize":{"type":"number","description":"Number of records to process per batch. Controls how many records are sent\nto NetSuite in a single API call. Typical values: 50-200.\n"},"internalIdLookup":{"type":"object","description":"Configuration for looking up existing NetSuite records to match against incoming data.\nRequired when operation is \"update\", \"addupdate\", or \"delete\".","properties":{"extract":{"type":"string","description":"Path in the source record to extract the lookup value from (e.g. \"internalId\" or \"externalId\")."},"searchField":{"type":"string","description":"NetSuite field to search against (e.g. \"externalId\", \"email\", \"name\", \"tranId\")."},"operator":{"type":"string","description":"Comparison operator for the lookup (e.g. \"is\", \"contains\", \"startswith\")."},"expression":{"type":"string","description":"NetSuite search expression for complex lookup conditions, used for multi-field or conditional lookups."}}},"hooks":{"type":"object","description":"Script hooks for custom processing at different stages of the import.\nEach hook references a SuiteScript file and function.\n","properties":{"preMap":{"type":"object","description":"Runs before field mapping is applied.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}},"postMap":{"type":"object","description":"Runs after field mapping, before submission to NetSuite.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}},"postSubmit":{"type":"object","description":"Runs after the record is submitted to NetSuite.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}}}},"mapping":{"type":"object","description":"NetSuite-specific Mapper 1.0 field mappings (the \"DA\" dialect).\nUsed by `NetSuiteDistributedImport` exclusively — NetSuite's\nDistributed Adaptor cannot consume generic v1 mappings or the\nMapper 2.0 shape because it needs sublist, subrecord, and\nconditional lookup semantics the generic schemas don't model.\n\nField-level shape: `fields` for body columns, `lists` for\nsublists / line-item arrays.","properties":{"fields":{"type":"array","items":{"type":"object","properties":{"extract":{"type":["string","null"],"description":"Path in the source record to extract the value from.\nUse dot notation for nested fields (e.g., \"address.city\").\nThe platform stores `null` here on fields driven by\n`hardCodedValue` instead of an extract path.\n"},"generate":{"type":"string","description":"The NetSuite field ID to write the value to (e.g., \"companyname\", \"email\", \"subsidiary\").\n"},"hardCodedValue":{"type":"string","description":"A static value to always use instead of extracting from source data.\nMutually exclusive with extract.\n"},"lookupName":{"type":"string","description":"Reference to a lookup defined in netsuite_da.lookups by name."},"dataType":{"type":"string","description":"Data type hint for the field value (e.g., \"string\", \"number\", \"date\", \"boolean\").\n"},"internalId":{"type":"boolean","description":"When true, the value is a NetSuite internal ID reference."},"immutable":{"type":"boolean","description":"When true, this field is only set on record creation, not on updates."},"discardIfEmpty":{"type":"boolean","description":"When true, skip this field mapping if the extracted value is empty."},"extractDateFormat":{"type":"string","description":"Date format of the extracted value (e.g., \"MM/DD/YYYY\", \"ISO8601\").\nUsed to parse date strings from source data.\n"},"extractDateTimezone":{"type":["string","null"],"description":"Timezone of the extracted date value (e.g., \"America/New_York\"). Stored as `null` when not configured."},"subRecordMapping":{"type":"object","description":"Nested Mapper 1.0 mapping for a NetSuite subrecord\nembedded in this field.  A subrecord is a child\nrecord that only exists inside a parent (e.g. the\nInventory Detail on a serialized-item line of a\nsales order); NetSuite reaches it through the\nparent.\n\n**Recursive structure.**  The nested `mapping`\nobject has the same `{fields, lists}` shape as the\ntop-level `netsuite_da.mapping`.  Recursion is\nallowed but rare.","properties":{"recordType":{"type":"string","description":"NetSuite's internal record-type id of the\nSUBRECORD (not the parent).  E.g.\n`inventorydetail`, `itempricing`,\n`mainaddress`.\n"},"jsonPath":{"type":"string","description":"JSONPath into the source record that selects the\ndata feeding this subrecord.  Semantics depend\non placement:\n* Line-level (on `lists[].fields[].`\n  `subRecordMapping`) — often `$` to reuse\n  the enclosing sublist line's row, or a path\n  into a nested array on that row.\n* Body-level (on `fields[].subRecordMapping`)\n  — a path into the source record root.\nDefaults to `$`.\n"},"mapping":{"type":"object","description":"Nested `{fields, lists}` mapping for the\nsubrecord.  Identical shape to the top-level\n`netsuite_da.mapping`.  Subrecords often have\nempty `fields` and only a sublist (e.g.\n`inventorydetail` → `inventoryassignment`).\n"},"lookups":{"type":"array","description":"Subrecord-scoped lookups.  Same shape as the\ntop-level `netsuite_da.lookups`.  Fields inside\nthis `subRecordMapping.mapping` reference them\nvia `lookupName`.  Often `[]`.\n"},"referenceFieldId":{"type":"string","description":"Secondary reference-field identifier used on a\nsmall fraction of subrecord blocks in production\n(~3%).  Exact semantics are not publicly\ndocumented by Celigo — match the value on\nexisting resources when present; do NOT\nsynthesize one.  Leave unset unless a template\nclearly requires it.\n"}}},"conditional":{"type":"object","description":"Conditional logic for when to apply this field mapping\n(\"Only perform mapping when\" in the UI). Lookup-based\nconditions evaluate the lookup named by `lookupName`,\nwhich must be defined on this import.\n","properties":{"lookupName":{"type":"string","description":"Lookup to evaluate for the condition. Must exactly match the\n`name` of a lookup defined on this import."},"when":{"type":"string","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","ignore_if_set"],"description":"Condition that controls when this field mapping is applied."}}}}},"description":"Body-level field mappings. Each entry maps a source field to a NetSuite body field.\n"},"lists":{"type":"array","items":{"type":"object","properties":{"generate":{"type":"string","description":"The NetSuite sublist ID (e.g., \"item\" for sales order line items,\n\"addressbook\" for address sublists).\n"},"jsonPath":{"type":"string","description":"LEGACY — do not set. Nominally the JSON path to the array of sublist records, but NetSuite DA ignores a top-level jsonPath here."},"fields":{"type":"array","items":{"type":"object","properties":{"extract":{"type":["string","null"],"description":"Path from the source record root that produces\nthe value for this sublist column. Paths are\nalways anchored at the source record root —\nnever relative to a pre-iterated row. The\nplatform stores `null` here on fields driven by\n`hardCodedValue` instead of an extract path."},"generate":{"type":"string","description":"NetSuite sublist field ID to write to."},"hardCodedValue":{"type":"string","description":"Static value for this sublist field."},"lookupName":{"type":"string","description":"Reference to a lookup by name."},"dataType":{"type":"string","description":"Data type hint for the field value."},"internalId":{"type":"boolean","description":"When true, the value is a NetSuite internal ID reference."},"isKey":{"type":"boolean","description":"When true, this field is a key field for matching existing sublist lines."},"immutable":{"type":"boolean","description":"When true, this field is only set on record creation, not on updates."},"discardIfEmpty":{"type":"boolean","description":"When true, skips this field mapping if the extracted value is empty."},"extractDateFormat":{"type":"string","description":"Date format of the extracted value."},"extractDateTimezone":{"type":["string","null"],"description":"Timezone of the extracted date value. Stored as `null` when not configured."},"subRecordMapping":{"type":"object","description":"Nested Mapper 1.0 mapping for a NetSuite\nsubrecord embedded in this sublist-field.  A\nsubrecord is a child record that only exists\ninside a parent (e.g. the Inventory Detail on\na serialized-item line of a sales order);\nNetSuite reaches it through the parent.\n\n**Recursive structure.**  The nested\n`mapping` object has the same\n`{fields, lists}` shape as the top-level\n`netsuite_da.mapping`.  Recursion is allowed\nbut rare.","properties":{"recordType":{"type":"string","description":"NetSuite's internal record-type id of the\nSUBRECORD (not the parent).  E.g.\n`inventorydetail`, `itempricing`,\n`componentinventorydetail`.\n"},"jsonPath":{"type":"string","description":"JSONPath (relative to the current sublist\nline) to the rows that populate the\nsubrecord.  Typically `$` when the\nsubrecord data is carried on the same\nline item (the enclosing\n`lists[].jsonPath` has already selected\nthe line).  Defaults to `$`.\n"},"mapping":{"type":"object","description":"Nested `{fields, lists}` mapping for the\nsubrecord.  Identical shape to the\ntop-level `netsuite_da.mapping`.\nSubrecords often have empty `fields` and\nonly a sublist (e.g. `inventorydetail` →\n`inventoryassignment`).\n"},"lookups":{"type":"array","description":"Subrecord-scoped lookups.  Same shape as\nthe top-level `netsuite_da.lookups`.\nFields inside this\n`subRecordMapping.mapping` reference\nthem via `lookupName`.  Often `[]`.\n"},"referenceFieldId":{"type":"string","description":"Secondary reference-field identifier used\non a small fraction of subrecord blocks\nin production (~3%).  Exact semantics are\nnot publicly documented by Celigo — match\nthe value on existing resources when\npresent; do NOT synthesize one.  Leave\nunset unless a template clearly requires\nit.\n"}}},"conditional":{"type":"object","description":"Conditional logic for when to apply this sublist field mapping\n(\"Only perform mapping when\" in the UI). Lookup-based conditions\nevaluate the lookup named by `lookupName`, which must be defined\non this import.","properties":{"lookupName":{"type":"string","description":"Lookup to evaluate for the condition. Must exactly match the\n`name` of a lookup defined on this import."},"when":{"type":"string","description":"Condition that controls when this field mapping is applied.","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","ignore_if_set"]}}}}},"description":"Field mappings for each column in the sublist."}}},"description":"Sublist (line-item) mappings. Each entry maps source data to a NetSuite sublist.\n"}}},"lookups":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia `lookupName` and Handlebars expressions reference it via\n`{{lookup \"name\" value}}`. Must be unique within this\nresource's `lookups` array."},"recordType":{"type":"string","description":"NetSuite record type to search. Use the internal record-type\nid (lowercase, underscored), e.g. `customer`, `item`,\n`salesorder`, `customlist_xxx`."},"searchField":{"type":"string","description":"NetSuite field on `recordType` to match against. Use the\ninternal field id — e.g. `email`, `externalid`,\n`internalid`, `entityid`, `custitem_xxx`."},"resultField":{"type":"string","description":"NetSuite field whose value is returned when a record matches.\nTypically `internalid` when the lookup resolves to a record\nreference, or any queryable scalar field."},"expression":{"type":"string","description":"Optional advanced-search expression (NetSuite saved-search\nDSL) for multi-criterion lookups. When present, supersedes\n`searchField` / `operator` — those fields are ignored."},"operator":{"type":"string","description":"Comparison operator for the `searchField` match. Common\nvalues: `is`, `contains`, `startswith`, `anyof`,\n`equalto`, `greaterthan`."},"includeInactive":{"type":"boolean","description":"When true, includes inactive records in lookup results."},"useDefaultOnMultipleMatches":{"type":"boolean","description":"Controls behaviour when the search matches more than one\nrecord. When true, the `default` value is returned instead\nof raising an ambiguous-match error. When false (default),\nmultiple matches are treated as a failure."},"allowFailures":{"type":["boolean","null"],"description":"When true, a lookup miss (no matching record and no\n`default`) resolves to `null` and the import continues.\nWhen false (default), a miss halts the record. May be\nstored as null (treated as unset)."},"map":{"type":["object","null"],"description":"Optional static key→value object evaluated BEFORE the\nNetSuite search. When the incoming value matches a key, the\nmapped value is returned without hitting NetSuite. Null on\ndynamic (search-based) lookups with no static map."},"default":{"type":["string","null"],"description":"Value returned when the search matches no records. When\nomitted and `allowFailures` is false, an unmatched lookup\nhalts the record. May be stored as null (no default)."}}},"description":"Lookup definitions that resolve reference values from NetSuite.\nReferenced by name from field mappings via `lookupName` and from\nHandlebars expressions via `{{lookup \"name\" value}}`."},"rawOverride":{"type":"object","description":"Raw override object for advanced use cases. When useRawOverride is true,\nthis object is sent directly to the NetSuite API, bypassing normal mapping.\n"},"useRawOverride":{"type":"boolean","description":"When true, uses rawOverride instead of the normal mapping configuration.\n"},"isMigrated":{"type":"boolean","description":"When true, this import was migrated from a legacy format. Set by the system."},"retryUpdateAsAdd":{"type":"boolean","description":"When true, if an update fails because the record doesn't exist, automatically\nretry as an add operation. Useful for initial syncs where records may not exist yet.\n"},"customFieldMetadata":{"type":"object","description":"Metadata about custom fields on the target record type.\nPopulated by the system from NetSuite metadata — do not set manually.\n"},"file":{"type":"object","description":"File cabinet configuration for file-based imports into NetSuite.\n","properties":{"name":{"type":"string","description":"Filename for the file in NetSuite File Cabinet."},"fileType":{"type":"string","description":"NetSuite file type (e.g., \"PDF\", \"CSV\", \"PLAINTEXT\", \"EXCEL\", \"XML\").\n"},"folder":{"type":"string","description":"Folder path or name in the NetSuite File Cabinet."},"folderInternalId":{"type":"string","description":"Internal ID of the target folder in NetSuite File Cabinet."},"internalId":{"type":"string","description":"Internal ID of an existing file to update."},"backupFolderInternalId":{"type":"string","description":"Internal ID of a backup folder for file versioning."}}},"isFileProvider":{"type":"boolean","description":"Whether this import handles files in the NetSuite File Cabinet.\n"},"preferences":{"type":"object","properties":{"ignoreReadOnlyFields":{"type":"boolean","description":"When true, silently skip read-only fields instead of raising errors."},"warningAsError":{"type":"boolean","description":"When true, treat NetSuite warnings as errors that stop the import."},"skipCustomMetadataRequests":{"type":"boolean","description":"When true, skip fetching custom field metadata to improve performance."}},"description":"Import behavior preferences that control how NetSuite handles the import operation.\n"},"recordTypeId":{"type":"string","description":"The internal record type ID. Used for custom record types in NetSuite where\nthe numeric ID is needed in addition to the recordType string.\n"}},"if":{"not":{"propertyNames":{"enum":["lookups","mapping","missingOrCorruptedDAConfig"]}}},"then":{"required":["operation","recordType"],"if":{"properties":{"operation":{"enum":["update","addupdate","delete"]}},"required":["operation"]},"then":{"required":["internalIdLookup"]}}},"Rdbms":{"type":"object","description":"Configuration for RDBMS imports into SQL Server, MySQL, PostgreSQL, Snowflake, Oracle, MariaDB, and other relational databases.\nqueryType determines which companion field is required: per_record, per_page, and first_page use the query field; bulk_insert uses the bulkInsert object; bulk_load uses the bulkLoad object — never set query and bulkInsert together.\n(The server itself stores an empty `query: []` next to bulk_insert/bulk_load configs and may leave an empty `bulkInsert: {}` stub next to others — empty companions are normal in responses; only a populated conflicting companion is invalid.)\nThe query field is an array of plain SQL strings, not a single string and not an array of objects.","properties":{"lookups":{"$ref":"#/components/schemas/Lookups","description":"Lookup definitions executed against the RDBMS connection. Each\nentry runs a `SELECT` and extracts a value used by field mappings\n(referenced via `lookupName`) or Handlebars expressions\n(referenced via `{{lookup \"name\" value}}`).\n\n**Mirror invariant**\nThe Celigo platform persists lookups in TWO locations on an RDBMS\nimport: top-level `lookups` and `rdbms.lookups`. Both arrays\nmust hold byte-for-byte identical content — the MappingsAgent\nwrites both on every save.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia `lookupName` and Handlebars expressions reference it via\n`{{lookup \"name\" value}}`. Must be unique within this\nresource's `lookups` array.\n"},"query":{"type":"string","description":"SQL `SELECT` statement that resolves the lookup value.\nTypically returns a single row with a single column; use\n`extract` to pick the column when the query returns multiple.\nInject values from the incoming record with Handlebars — RDBMS\nqueries use triple braces and the `record.` prefix:\n`SELECT id FROM users WHERE email = '{{{record.email}}}'`.\n"},"extract":{"type":"string","description":"JSONPath-style expression that selects the value from the query\nresult row (e.g. `id`, `details.price`). Omit when the\nquery returns a single scalar — the first column is used\nautomatically.\n"},"_lookupCacheId":{"type":"string","format":"objectId","description":"Optional reference to a LookupCache resource. When set, the\nlookup reads from the cache instead of issuing a live SQL\nquery — useful for stable reference data that should be\npre-loaded.\n"},"map":{"type":"object","description":"Optional static key→value object evaluated BEFORE the SQL query.\nWhen the incoming value matches a key, the mapped value is\nreturned without querying the database. Useful for small\ncontrolled enumerations.\n"},"default":{"type":"string","description":"Value returned when the query matches no rows. When omitted and\n`allowFailures` is false, an unmatched lookup halts the\nrecord.\n"},"allowFailures":{"type":"boolean","description":"When true, a lookup miss (no matching row and no `default`)\nresolves to `null` and the import continues. When false\n(default), a miss halts the record.\n"}}},"type":"array"},"query":{"type":"array","items":{"type":"string"},"description":"SQL statements to execute; required when queryType is `[\"per_record\"]`, `[\"per_page\"]`, or `[\"first_page\"]`, and typically contains a single statement.\nEach element is a complete SQL statement as a plain string — never a single bare string and never an array of objects (objects cause a Cast error).\nInject values from the incoming record with Handlebars using the `record.` prefix and triple braces, e.g. `'{{{record.name}}}'` for strings (single-quoted in the SQL) and `{{{record.quantity}}}` for numbers.\nRendering is strict: a reference the render model cannot resolve (a bare `{{name}}` without prefix, or `record.*` when the arriving record is a grouped array) fails the step with `cannot_evaluate_handlebars` — it does not render an empty value.\n`record` and `rows` are aliases chosen by the arriving data's shape: an object record binds `record` (`rows` undefined); a grouped array-record (e.g. upstream `groupByFields`) binds `rows` (`record` undefined) — address grouped rows as `{{rows.0.field}}` or iterate `{{#each rows}}`.\nDouble braces (`{{record.field}}`) output the value wrapped in single quotes with embedded quotes doubled, while triple braces output it raw; block helpers such as `{{#each}}` and `{{#if}}` use double braces as normal."},"queryType":{"type":["array","null"],"items":{"type":"string","enum":["INSERT","UPDATE","bulk_insert","per_record","per_page","first_page","bulk_load"]},"description":"Execution strategy for the SQL operation, which determines the required companion field: per_record, per_page, and first_page require `query`; bulk_insert requires `bulkInsert`; bulk_load requires `bulkLoad`.\nPrefer the highest-performance type the operation supports: bulk_load (currently Snowflake and NSAW — handles INSERT, upsert via `bulkLoad.primaryKeys`, and custom merge or ignore-existing logic via `bulkLoad.overrideMergeQuery`), then bulk_insert for pure INSERTs, then per_page, then per_record only when the logic cannot be expressed as a batch operation.\nFor UPDATE, UPSERT, or ignore-existing logic on databases without bulk_load support, use per_page or per_record with `query` — bulk_insert has no duplicate-checking logic.\nSet a single value; do not combine types or use the legacy INSERT and UPDATE values.\nper_page statements render against the whole batch (`batch_of_records`) rather than a single record — loop with `{{#each batch_of_records}}...{{{record.fieldName}}}...{{/each}}`.\n\n`bulk_load` — Stages data as a file and loads via database-native COPY/bulk mechanism for maximum throughput; supports INSERT, upsert via bulkLoad.primaryKeys, and custom merge or ignore-existing logic via bulkLoad.overrideMergeQuery; currently supported for Snowflake and NSAW.\n`bulk_insert` — Batch INSERT via multi-row VALUES clause for efficient bulk data loading; has no upsert or ignore-existing logic and is available for all RDBMS types.\n`per_page` — Executes one SQL statement per page (batch) of records; available for all RDBMS types.\n`per_record` — Executes one SQL statement per record, giving full control over individual record SQL; available for all RDBMS types."},"bulkInsert":{"type":"object","description":"Configuration for batch INSERTs via a multi-row VALUES clause; required when queryType is `[\"bulk_insert\"]` and must not be set otherwise.\nOnly suited to pure INSERTs — for UPDATE, UPSERT, or ignore-existing/skip-duplicates logic, use queryType `[\"per_record\"]` with the `query` field instead.","properties":{"tableName":{"type":"string","description":"The name of the database table into which the bulk insert operation will be executed. This value must correspond to a valid, existing table within the target relational database management system (RDBMS). It serves as the primary destination for inserting multiple rows of data efficiently in a single operation. The table name can include schema or namespace qualifiers if supported by the database (e.g., \"schemaName.tableName\"), allowing precise targeting within complex database structures. Proper validation and sanitization of this value are essential to ensure the operation's success and to prevent SQL injection or other security vulnerabilities."},"batchSize":{"type":"string","description":"The number of records to be inserted into the database in a single batch during a bulk insert operation. This parameter is crucial for optimizing the performance and efficiency of bulk data loading by controlling how many records are grouped together before being sent to the database. Proper tuning of batchSize balances memory consumption, transaction overhead, and throughput, enabling the system to handle large volumes of data efficiently without overwhelming resources or causing timeouts. Adjusting batchSize directly impacts transaction size, network utilization, error handling granularity, and recovery strategies, making it essential to tailor this value based on the specific database capabilities, system resources, and workload characteristics."}}},"bulkLoad":{"type":"object","properties":{"tableName":{"type":"string","description":"Target table for the bulk load.\nMust reference an existing table accessible with the connection's credentials; schema-qualified names are supported."},"primaryKeys":{"type":"array","items":{"type":"string"},"description":"Columns that uniquely identify each record in the target table, used to match existing rows during the load.\nSet for upsert behavior (a MERGE is auto-generated); omit (or empty) for a pure INSERT.\nComposite keys are supported — list every key column, and names must exactly match the target schema."},"overrideMergeQuery":{"type":"boolean","description":"When true, a custom merge query replaces the auto-generated MERGE statement, enabling ignore-existing logic, conditional updates, or multi-table operations.\nThe override SQL references `{{import.rdbms.bulkLoad.preMergeTemporaryTable}}` for the staging table."}},"description":"Configuration for bulk loading: data is staged as a file and loaded via the database's native COPY/bulk mechanism for maximum throughput.\nRequired when queryType is `[\"bulk_load\"]`; currently supported for Snowflake and NSAW."},"updateLookupName":{"type":["string","null"],"description":"Name of the lookup used for update operations in the legacy composite (queryType1) flow."},"updateExtract":{"type":["string","null"],"description":"Path used to extract the value that drives update operations in the legacy composite flow."},"ignoreLookupName":{"type":["string","null"],"description":"Name of the lookup used to determine which records to ignore when ignore-existing is enabled."},"ignoreExtract":{"type":["string","null"],"description":"Path used to extract the value that determines whether a record is ignored when ignore-existing is enabled."}},"if":{"not":{"propertyNames":{"enum":["lookups"]}}},"then":{"required":["queryType"],"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"enum":["per_record","per_page"]}}}},"then":{"required":["query"],"properties":{"bulkInsert":{"maxProperties":0}}},"else":{"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"const":"first_page"}}}},"then":{"required":["query"]},"else":{"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"const":"bulk_insert"}}}},"then":{"required":["bulkInsert"],"properties":{"query":{"maxItems":0}}},"else":{"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"const":"bulk_load"}}}},"then":{"required":["bulkLoad"],"properties":{"query":{"maxItems":0}}}}}}}},"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 set to true\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\nIf allowFailures is false and no default is specified, the flow will fail\nwhen encountering unmapped values.\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\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\nThe best practice is typically to set allowFailures to true with a meaningful\ndefault value, so flows remain operational while alerting you to missing mappings.\n"}}}},"S3":{"type":"object","description":"Defines where files are written to 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 destination, fileKey sets the object key, and backupBucket\nretains a copy after a successful import.","required":["region","bucket"],"properties":{"region":{"type":"string","default":"us-east-1","description":"AWS region where the S3 bucket resides. Must match the bucket's actual location to avoid\nconnectivity errors and misrouted requests."},"bucket":{"type":"string","description":"S3 bucket that receives the imported files. The bucket must already exist and the\nconnection's AWS credentials must have s3:PutObject permission on it."},"fileKey":{"type":"string","description":"Object key under which each file is stored in the bucket. Include slashes to organize\nfiles in a folder-style hierarchy (for example, imports/2023/12/orders.json); keys are\ncase-sensitive. Supports handlebars placeholders such as orders-{{timestamp}}.json to\ngenerate a unique key per run."},"backupBucket":{"type":"string","description":"S3 bucket where a copy of each imported file is retained after a successful import; if\nomitted, no backup copy is kept. Must reference an existing bucket the connection's\ncredentials have s3:PutObject permission on."},"serverSideEncryptionType":{"type":"string","description":"Server-side encryption applied to uploaded objects. The connection form sets `AES256`\n(SSE-S3, S3-managed keys) when encryption is enabled; leave unset to use the bucket's\ndefault encryption."}}},"Wrapper":{"type":"object","description":"Configuration for Wrapper imports, which delegate writing records 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.","properties":{"function":{"type":"string","description":"Name of the function the wrapper invokes to process records.\nMust match a callable function in the wrapper's execution context; names are case-sensitive."},"configuration":{"type":"object","additionalProperties":true,"description":"Free-form settings passed to the wrapper function at runtime (connector-specific keys such\nas method, apiVersion, headers, or handler). Structure is defined by the wrapper code, not\nby this schema."},"lookups":{"$ref":"#/components/schemas/Lookups","description":"Lookup definitions for this wrapper import. Wrapper lookups use\nthe generic Celigo shape: either a static `map` (key→value\nobject) or a dynamic `_lookupCacheId` + `extract` pair. See\n`common/schemas/lookups.yml` for the full item shape."}},"if":{"not":{"propertyNames":{"enum":["lookups"]}}},"then":{"required":["function"]}},"Salesforce":{"type":"object","description":"Salesforce-specific configuration for the import: the operation to perform, the API to use, and object-level settings such as `sObjectType`, `idLookup`, and `upsert`. Properties like `ignoreExisting`, `ignoreMissing`, `name`, and `description` are not part of this object — they belong at the import level.","properties":{"lookups":{"description":"Lookup definitions used to resolve Salesforce record references or\nstatic value mappings. Each lookup has a unique ``name`` that mapping\nfields reference via ``lookupName`` and that Handlebars expressions\nreference via ``{{lookup \"name\" value}}``.\n\n**Mirror invariant**\nThe Celigo platform stores lookups in TWO locations on a Salesforce\nimport resource: top-level ``lookups`` and ``salesforce.lookups``.\nBoth arrays must hold byte-for-byte identical content — the\nMappingsAgent writes both on every save.\n\n**Static vs dynamic lookups**\n- **Static** — only ``map`` + optional ``default``. No Salesforce\n  query runs. Use for small controlled vocabularies (country codes,\n  status labels, etc.).\n- **Dynamic** — ``sObjectType`` + ``resultField`` + ``whereClause``.\n  Issues a SOQL query at runtime. Use ``map`` alongside the query to\n  short-circuit known values before hitting Salesforce.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup entry. Mapping fields reference\nit via ``lookupName``, and Handlebars expressions reference it\nvia ``{{lookup \"name\" value}}``. Must be unique within this\nresource's ``lookups`` array. Conventionally a short slug or a\ngenerated hash — the value is only meaningful as a reference\ntoken.\n"},"sObjectType":{"type":"string","description":"Salesforce sObject API name to query (e.g. ``Account``,\n``Contact``, ``Solution__c``). For custom objects include the\n``__c`` suffix. Case-sensitive — must match exactly as declared\nin the target Salesforce org.\n"},"resultField":{"type":"string","description":"sObject field whose value is returned when the SOQL ``whereClause``\nmatches a record. Typically ``Id`` when the lookup resolves to a\nSalesforce record reference, but any queryable field is valid.\n"},"whereClause":{"type":"string","description":"SOQL ``WHERE`` clause that identifies the matching Salesforce\nrecord. Use Handlebars expressions (triple braces recommended:\n``{{{field}}}``) to inject values from the incoming source record.\nWrap the whole expression in parentheses.\n\nExamples:\n- ``(Id = {{{id [Customer SFDC ID]}}})``\n- ``(Email = '{{{email}}}')``\n- ``(External_Id__c = {{{string externalId}}} AND IsActive = true)``\n\nCeligo's Handlebars helpers (``string``, ``id``, ``double``,\n``bool``, ``date``) produce correctly-typed SOQL literals —\nprefer them over manual quoting. String values wrap in single\nquotes; numeric/id values do not.\n"},"map":{"type":["object","null"],"description":"Optional static key→value map evaluated BEFORE the SOQL query.\nWhen the incoming field value matches a key, the mapped value\nis returned without hitting Salesforce. Useful for small\ncontrolled enumerations where a full query would be overkill.\n"},"default":{"type":["string","null"],"description":"Value returned when the SOQL query matches no records. When\nomitted and ``allowFailures`` is false, an unmatched lookup\nfails the record.\n"},"allowFailures":{"type":"boolean","description":"When true, a lookup miss (no matching record and no ``default``)\nis tolerated — the mapped field resolves to ``null`` and the\nimport continues. When false (default), a miss halts processing\nfor the record.\n"},"useDefaultOnMultipleMatches":{"type":"boolean","description":"Controls behaviour when the SOQL query matches more than one\nrecord. When true, the ``default`` value is returned instead of\nraising an ambiguous-match error. When false (default), multiple\nmatches are treated as a failure.\n"}}},"type":"array"},"operation":{"type":"string","enum":["insert","update","upsert","upsertpicklistvalues","delete","addupdate"],"description":"Controls how records are written to Salesforce. Each value pairs with a lookup strategy: `upsert` requires `upsert.externalIdField` plus `idLookup.extract`, while `update`, `delete`, `addupdate`, and `insert` with `ignoreExisting` require `idLookup.whereClause`. Values are converted to lowercase automatically."},"api":{"type":"string","enum":["soap","rest","metadata","compositerecord"],"description":"Selects the Salesforce API used to execute the import. Use `soap` for most imports and another API only when its specific capability is needed; default to `soap` when no API is specified. Values are converted to lowercase automatically."},"soap":{"type":"object","properties":{"headers":{"type":"object","properties":{"allOrNone":{"type":"boolean","description":"When true, the batch is processed atomically: if any record fails, the entire transaction is rolled back and nothing is committed. When false or omitted, records that succeed are committed even if others in the batch fail."}},"description":"SOAP headers included in requests sent to Salesforce, such as the `allOrNone` transaction-control header."},"batchSize":{"type":"number","description":"Number of records sent to Salesforce in each SOAP API batch. Larger batches reduce the number of API calls; smaller batches reduce per-call load and the chance of timeouts."}},"description":"Settings that apply when `api` is `soap`, including SOAP request headers and batch size."},"sObjectType":{"type":"string","description":"Salesforce object the import writes to, such as `Account`, `Contact`, or a custom object like `Vendor__c`. Must exactly match the object's case-sensitive API name; custom objects include the `__c` suffix."},"idLookup":{"type":"object","properties":{"extract":{"type":"string","description":"Field in the incoming data whose value is matched against the Salesforce External ID field named in `upsert.externalIdField`. Set only when `operation` is `upsert`; all other operations use `whereClause` instead. Choose a field that is unique and always populated — missing or null values cause the upsert to fail. If both `extract` and `whereClause` are set, `extract` takes precedence for upsert."},"whereClause":{"type":"string","description":"SOQL condition (without the `WHERE` keyword) that finds existing Salesforce records for each incoming record. Required when `operation` is `update`, `delete`, or `addupdate`, and for `insert` with `ignoreExisting`; for `upsert`, use `extract` instead. Reference incoming fields with triple-brace Handlebars and wrap string values in single quotes (e.g. `Email = '{{{Email}}}'`) — triple braces emit the raw value without HTML-escaping, which would corrupt characters like `&` or `'`. Combine conditions with `AND`/`OR`."}},"description":"Controls how existing Salesforce records are matched before writing. Set exactly one of `extract` (for `upsert`) or `whereClause` (for `update`, `delete`, `addupdate`, and `insert` with `ignoreExisting`); omit for plain inserts.\n\nRequired when operation is addupdate, delete, update, or upsert."},"upsert":{"type":"object","properties":{"externalIdField":{"type":"string","description":"API name of the Salesforce field, marked as External ID, that upsert operations match against. This is the target side of the match; `idLookup.extract` names the source field in the incoming data. If the value matches multiple Salesforce records, the upsert fails."}},"description":"Upsert matching configuration, required when `operation` is `upsert` and omitted for all other operations. Contains only `externalIdField`, which pairs with `idLookup.extract` at the parent level.\n\nRequired when operation is upsert."},"upsertpicklistvalues":{"type":"object","properties":{"type":{"type":"string","enum":["picklist","multipicklist"],"description":"Picklist field type being managed. Values are converted to lowercase automatically."},"fullName":{"type":"string","description":"Fully qualified API name of the picklist field, in `ObjectName.FieldName__c` form (for example, `Account.MyPicklist__c`). Must match the case-sensitive Salesforce API name."},"label":{"type":"string","description":"Display label for the picklist field in the Salesforce UI. Can contain spaces and does not need to match the API name."},"visibleLines":{"type":"number","description":"Number of values visible without scrolling in the Salesforce UI selection box. Only applies when `type` is `multipicklist`."}},"description":"Picklist field definition managed when `operation` is `upsertpicklistvalues`; omit for all other operations. Manages the field's metadata — name, label, and display settings — and requires Salesforce Metadata API permissions.\n\nRequired when operation is upsertpicklistvalues."},"removeNonSubmittableFields":{"type":"boolean","description":"When true, strips fields Salesforce will not accept — read-only, system-generated, and formula fields — from the payload before submission, preventing rejection errors. When false or omitted, all fields are submitted as-is. Only the outgoing payload is affected; the source data is unchanged."},"document":{"type":"object","properties":{"id":{"type":"string","description":"Salesforce-assigned ID of the Document record. The 15-character form is case-sensitive; the 18-character form adds a case-insensitivity checksum."},"name":{"type":"string","description":"Display name of the document in Salesforce, used to identify it in folders, search, and API operations."},"folderId":{"type":"string","description":"Salesforce folder that stores the document. The document inherits the folder's sharing and visibility settings; changing this value moves the document to a different folder."},"contentType":{"type":"string","description":"MIME type of the document content, such as `application/pdf` or `image/png`."},"developerName":{"type":"string","description":"Unique API name used to reference the document in code, metadata, and integrations. Distinct from the display name; uses only letters, numbers, and underscores with no spaces."},"isInternalUseOnly":{"type":"boolean","description":"When true, marks the document for internal use only — it must not be shared with parties outside the organization."},"isPublic":{"type":"boolean","description":"When true, the document is accessible to all users; when false, access is limited to authorized users by Salesforce permissions and sharing settings."}},"description":"Salesforce Document record written by the import, combining the file content with metadata such as name, folder, and content type."},"attachment":{"type":"object","properties":{"id":{"type":"string","description":"Salesforce-assigned ID of the Attachment record. Prefer the 18-character case-insensitive form over the 15-character case-sensitive form in integrations."},"name":{"type":"string","description":"Filename of the attachment, including the file extension (for example, `contract_agreement.pdf`)."},"parentId":{"type":"string","description":"Salesforce record the attachment is linked to, such as an Account, Contact, or Opportunity. Required when creating an attachment; access to the attachment follows the parent record's permissions."},"contentType":{"type":"string","description":"MIME type of the attachment content, such as `application/pdf` or `image/png`."},"isPrivate":{"type":"boolean","description":"When true, the attachment is visible only to users explicitly authorized to access it; when false or omitted, it inherits the visibility of its parent record. This setting only restricts access further — it never grants access beyond the parent record's permissions."},"description":{"type":"string","description":"Free-text note describing the attachment's content or purpose, shown alongside the attachment details in Salesforce."}},"description":"Salesforce Attachment record written by the import — a file linked to a parent record via `parentId`, with metadata such as name and content type."},"contentVersion":{"type":"object","properties":{"contentDocumentId":{"type":"string","description":"ContentDocument this version belongs to; all versions of the same document share this ID. Assigned by Salesforce when the content version is created and immutable afterward."},"title":{"type":"string","description":"Display title of this content version, shown in content libraries, search results, and version history."},"pathOnClient":{"type":"string","description":"Original file path on the client machine before upload. Used for reference and display only; it does not affect how the file is stored or accessed in Salesforce."},"tagCsv":{"type":"string","description":"Comma-separated tags applied to the content version for categorization and search. Updating this field replaces the entire tag set, so include every tag the version should keep."},"contentLocation":{"type":"string","description":"Specifies where the content file for this ContentVersion record is stored — \"S\" for Salesforce internal storage or \"E\" for an external system (the two values the import form offers). Salesforce also defines \"L\" for content shared via a link, but the form does not set it. Typically assigned automatically based on the upload method."}},"description":"Salesforce ContentVersion record written by the import — a specific version of a content item in Salesforce Files. Each modification produces a new version, and all versions of a document are linked through `contentDocumentId`."}},"if":{"anyOf":[{"required":["attachment"]},{"required":["contentVersion"]}]},"then":{"required":["operation"]},"else":{"if":{"not":{"propertyNames":{"enum":["lookups","api"]}}},"then":{"required":["operation","api"],"if":{"properties":{"operation":{"const":"upsert"}},"required":["operation"]},"then":{"required":["idLookup","upsert"],"properties":{"idLookup":{"required":["extract"]},"upsert":{"required":["externalIdField"]}}},"else":{"if":{"properties":{"operation":{"enum":["update","delete","addupdate"]}},"required":["operation"]},"then":{"required":["idLookup"],"properties":{"idLookup":{"required":["whereClause"]}}},"else":{"if":{"properties":{"operation":{"const":"upsertpicklistvalues"}},"required":["operation"]},"then":{"required":["upsertpicklistvalues"],"properties":{"upsertpicklistvalues":{"required":["fullName"]}}}}}}}},"Tool":{"type":"object","description":"Configuration for a `ToolImport`, which executes a reusable Celigo Tool resource as the\nimport action. The referenced tool defines its own input contract, processing pipeline, and\noutput; this object binds the tool to the import and optionally overrides the connections the\ntool uses for this invocation.","required":["_toolId"],"properties":{"_toolId":{"type":"string","format":"objectId","description":"Tool resource this import executes. The tool supplies the input schema, routing, page\nprocessors, and output mapping; the import runs that tool when records reach it. Required —\na `ToolImport` has no behavior without a tool to run."},"overrides":{"type":"object","description":"Per-import overrides applied to the referenced tool at execution time. Tools use a\nbring-your-own-keys connection model: the tool declares abstract connection slots, and the\nconsumer binds concrete connections here so one tool definition can run against different\nsystems without being modified. Omit to run the tool with its default connections.","properties":{"connections":{"type":"array","description":"Connection bindings that replace the tool's abstract connection slots for this import.\nEach entry maps one of the tool's abstract connections to the concrete connection the\nimport should use.","items":{"type":"object","required":["_id","_abstractId"],"properties":{"_id":{"type":"string","format":"objectId","description":"Concrete connection bound to the abstract slot for this import's tool run."},"_abstractId":{"type":"string","format":"objectId","description":"Abstract connection slot, declared by the tool, that this binding replaces. Matches\nthe abstract connection id defined on the referenced tool."},"_borrowConcurrencyFromConnectionId":{"type":"string","format":"objectId","description":"Optional pointer to another connection whose\nconcurrency budget this binding shares. Use\nwhen several wrappers / steps should share rate\nlimits against the same downstream system but\nuse different credentials. Mutually exclusive\nwith `_id` on the same entry.\n"}}}}}}}},"File":{"type":"object","description":"**CRITICAL: This object is REQUIRED for all file-based import adaptor types.**\n\n**When to include this object**\n\n✅ **MUST SET** when `adaptorType` is one of:\n- `S3Import`\n- `FTPImport`\n- `AS2Import`\n- `HTTPImport` in file mode (`http.type: \"file\"` — the folder mode of the cloud file\n  providers such as Google Drive, Box, Dropbox, Azure Blob, Google Cloud Storage and Celigo\n  Storage); the same `file` object, including `type: filedefinition` with `_ediProfileId`\n  for EDI, applies as on S3 / FTP\n\n❌ **DO NOT SET** for non-file-based imports like:\n- `SalesforceImport`\n- `NetSuiteImport`\n- `HTTPImport` in records mode (`http.type` unset — a REST endpoint receiving records)\n- `MongodbImport`\n- `RDBMSImport`\n\n**Minimum required fields**\n\nFor most file imports, you need at minimum:\n- `fileName`: The output file name (supports Handlebars like `{{timestamp}}`)\n- `type`: The file format (json, csv, xml, xlsx)\n- `aggregation.type`: How the run's records are batched into files (`all` for one file per run)\n\n**Example (S3 Import)**\n\n```json\n{\n  \"file\": {\n    \"fileName\": \"customers-{{timestamp}}.json\",\n    \"aggregation\": { \"type\": \"all\" },\n    \"type\": \"json\"\n  },\n  \"s3\": {\n    \"region\": \"us-east-1\",\n    \"bucket\": \"my-bucket\",\n    \"fileKey\": \"customers-{{timestamp}}.json\"\n  },\n  \"adaptorType\": \"S3Import\"\n}\n```","properties":{"fileName":{"type":"string","description":"**REQUIRED for file-based imports.**\n\nThe name of the file to be created/written. Supports Handlebars expressions for dynamic naming.\n\n**Default behavior**\n- When the user does not specify a file naming convention, **always default to timestamped filenames** using `{{timestamp}}` (e.g., `\"items-{{timestamp}}.csv\"`). This ensures each run produces a unique file and avoids overwriting previous exports.\n- Only use a fixed filename (without timestamp) if the user explicitly requests overwriting or a fixed name.\n\n**Common patterns**\n- `\"data-{{timestamp}}.json\"` - Timestamped JSON file (DEFAULT — use this pattern when not specified)\n- `\"export-{{date}}.csv\"` - Date-stamped CSV file\n- `\"{{recordType}}-backup.xml\"` - Dynamic record type naming\n\n**Examples**\n- `\"customers-{{timestamp}}.json\"`\n- `\"orders-export.csv\"`\n- `\"inventory-{{date}}.xlsx\"`\n\n**Important**\n- For S3 imports, this should typically match the `s3.fileKey` value\n- For FTP imports, this should typically match the `ftp.fileName` value"},"skipAggregation":{"type":"boolean","description":"Legacy companion of `aggregation.type`: `true` when the run's records are written one\nfile per page, `false` when they are combined across the run or grouped by key.\nValidation reads the value sent — an EDI file definition needs `true` unless\n`aggregation.type` is `key`, and `batchSize` needs `true` — then the save rewrites it\nfrom `aggregation.type`, so send the two as a consistent pair.","default":false},"type":{"type":"string","enum":["json","csv","xml","xlsx","filedefinition"],"description":"**REQUIRED for file-based imports.**\n\nThe format of the output file.\n\nSelect the format that matches the target system's requirements."},"encoding":{"type":"string","enum":["utf8","win1252","utf-16le","gb18030","macroman","iso88591","shiftjis"],"description":"Character encoding for the output file.\n\n**Default:** `\"utf8\"`\n\nChange from the default only when the target system requires a specific encoding.","default":"utf8"},"delete":{"type":"boolean","description":"Whether to delete the source file after successful import.\n\n**Values**\n- `true`: Delete source file after processing\n- `false`: Keep source file\n\n**Default:** `false`","default":false},"compressionFormat":{"type":"string","enum":["gzip","zip"],"description":"Compression format for the output file.\n\n**When to use**\n- Large files that benefit from compression\n- When target system expects compressed files"},"backupPath":{"type":"string","description":"Path where backup copies of files should be stored.\n\n**Examples**\n- `\"backup/\"` - Relative backup folder\n- `\"/archive/2024/\"` - Absolute backup path"},"purgeInternalBackup":{"type":"boolean","description":"Whether to purge internal backup copies after successful processing.\n\n**Default:** `false`","default":false},"batchSize":{"type":"integer","description":"Number of records to include per batch/file when processing large datasets.\n\n**When to use**\n- Large imports that need to be split into multiple files\n- When target system has file size limitations\n\n**Note**\n- Offered when records are written one file per page (`aggregation.type: page` with\n  `skipAggregation: true`)"},"encrypt":{"type":"boolean","description":"Whether to encrypt the output file.\n\n**Values**\n- `true`: Encrypt the file (requires PGP configuration)\n- `false`: No encryption\n\n**Default:** `false`","default":false},"csv":{"type":"object","description":"CSV-specific configuration. Only used when `type` is `\"csv\"`.","properties":{"rowDelimiter":{"type":"string","description":"Character(s) used to separate rows. Default is newline.","default":"\n"},"columnDelimiter":{"type":"string","description":"Character(s) used to separate columns. Default is comma.","default":","},"includeHeader":{"type":"boolean","description":"When true, includes a header row with column names in the output.","default":true},"wrapWithQuotes":{"type":"boolean","description":"When true, wraps field values in quotes.","default":false},"replaceTabWithSpace":{"type":"boolean","description":"Replace tab characters with spaces.","default":false},"replaceNewlineWithSpace":{"type":"boolean","description":"Replace newline characters with spaces within fields.","default":false},"truncateLastRowDelimiter":{"type":"boolean","description":"Remove trailing row delimiter from the file.","default":false}}},"json":{"type":"object","description":"JSON-specific configuration. Only used when `type` is `\"json\"`.","properties":{"resourcePath":{"type":"string","description":"JSONPath expression to locate records within the JSON structure."}}},"xml":{"type":"object","description":"XML-specific configuration. Only used when `type` is `\"xml\"`.","properties":{"resourcePath":{"type":"string","description":"XPath expression to locate records within the XML structure."}}},"fileDefinition":{"type":"object","description":"Configuration settings for parsing files using a predefined file definition. This object enables processing of complex, non-standard, or proprietary file formats that require specialized parsing logic beyond what the standard parsers (CSV, JSON, XML, etc.) can handle.\n\n**When to use**\n\nConfigure this object when the `type` field is set to \"filedefinition\". This approach is required for properly handling:\n- Legacy or proprietary file formats with complex structures\n- Fixed-width text files where field positions are defined by character positions\n- Electronic Data Interchange (EDI) documents (X12, EDIFACT, etc.)\n- Multi-record type files where different lines have different formats\n- Files requiring complex preprocessing or custom parsing logic\n\n**File definition characteristics**\n\n- **Custom Parsing Rules**: Applies predefined parsing logic to complex file formats\n- **Reusable Configurations**: References externally defined parsing rules that can be reused\n- **Complex Format Support**: Handles formats that standard parsers cannot process\n- **Specialized Processing**: Often used for industry-specific or legacy formats\n\n**Implementation strategy for ai agents**\n\n1. **Format Analysis**:\n    - Determine if the file format is standard (CSV, JSON, XML) or requires custom parsing\n    - Check if the format follows industry standards like EDI, SWIFT, or fixed-width\n    - Assess if there are multiple record types within the same file\n    - Identify if specialized logic is needed to interpret the file structure\n\n2. **File Definition Selection**:\n    - Verify that a suitable file definition has already been created in the system\n    - Check if existing file definitions match the format requirements\n    - Confirm the file definition ID from system administrators if needed\n    - Ensure the file definition is compatible with the export's needs\n","properties":{"_fileDefinitionId":{"type":"string","format":"objectId","description":"Reference to the file definition resource."}}},"aggregation":{"type":"object","description":"Controls how a flow run's records are batched into output files — the transfer form's\n\"How many files would you like to generate?\" setting. The legacy `skipAggregation` flag\nmirrors `type` and is maintained by the platform.","properties":{"type":{"type":"string","enum":["all","page","key"],"description":"Which batches of a run's records become separate output files. EDI file definitions\n(`file.type: filedefinition`) are written per page or per key, never per run; `key`\nis accepted only for them and only when the account has B2B Manager. The grouping\nkey and the line-item loop it merges come from the EDI file definition's document\ntype, so there is no separate key field to configure."}}},"lookups":{"type":"array","description":"Named value-substitution maps available to this import's field mappings; each lookup\ntranslates source values to target values, with an optional default for unmatched values.\nReferenced by name from mapping rules.","items":{"type":"object","properties":{"name":{"type":"string","description":"Identifier used to reference this lookup from mapping rules."},"map":{"type":"object","description":"Source-value to target-value pairs applied during mapping.","additionalProperties":{"type":"string"}},"default":{"type":["string","null"],"description":"Value substituted when a source value is not found in map; empty string when omitted. May be stored as null (no default)."},"allowFailures":{"type":"boolean","description":"When true, a value missing from map does not fail the record and the default (or original value) is used."}}}},"pgp":{"type":"object","description":"PGP encryption settings applied when encrypt is true; selects the symmetric cipher and the\nsigning hash. The connection must hold the recipient's public key.","properties":{"symmetricKeyAlgorithm":{"type":"string","enum":["twofish","cast5","3des","aes128","aes192","aes256"],"default":"aes256","description":"Symmetric cipher used to encrypt the file body; aes256 unless the recipient requires another."},"hashAlgorithm":{"type":"string","enum":["sha256","sha384","sha512","sha224"],"description":"Hash algorithm used when signing the encrypted file; set only when the recipient requires a specific one."}}},"skipRename":{"type":"boolean","description":"When true, the import writes directly to the final file name instead of writing to a\ntemporary name and renaming on completion. Use only when the destination does not support\natomic rename; the default (false) uses the safe write-then-rename behavior."},"directory":{"type":"object","required":["pathMode"],"description":"Structured destination location for cloud file-provider imports (Google Drive shared\ndrives, Box, Dropbox), replacing the flat path used by classic FTP/S3 destinations. Select\nthe location by folder ID or by a path relative to a configured storage root via pathMode.\nFor these cloud providers, directoryId mode addresses the folder by id alone — the flat\ndestination-path field is not consulted at runtime.","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 destination directory (e.g. a Google Drive folder ID);\nset when pathMode is directoryId. This is the file provider's own ID, not a Celigo resource ID."},"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."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root the relative path resolves against; set when\npathMode is relativePath. The provider's own ID, not a Celigo resource ID."},"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 location for backup copies on cloud file providers — the file-provider\ncounterpart 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."}}}}},"FileSystem":{"type":"object","description":"Defines where files are written to 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 where generated files are written; the agent's OS\naccount must have write permission on it. Accepts a local OS path or a UNC network share,\nand supports handlebars templates for dynamic folders."}}},"AiAgentConfig":{"type":"object","description":"AI Agent configuration used by both AiAgentImport and GuardrailImport (ai_agent type).\n\nConfigures which AI provider and model to use, along with instructions, parameter\ntuning, output format, and available tools. Providers come in two families with\ntwo configuration formats:\n\n- **Built-in providers** — each has its own configuration block.\n  - **openai**: OpenAI models (GPT-4.1, GPT-5, etc.). Configure via the `openai` object.\n  - **gemini**: Google Gemini models via the LiteLLM proxy. Configure via `litellm` with overrides in `litellm._overrides.gemini`.\n  - **anthropic**: Anthropic Claude models via the LiteLLM proxy. Configure via `litellm` with overrides in `litellm._overrides.anthropic`.\n- **Catalog providers** — `mistral`, `xai`, `huggingface`, `deepseek`, `cohere`, `groq`. Added\n  as catalog data rather than platform code, they share one generic flat format:\n  `model`, `modelOptions`, `instructions`, `output`, and the `tools` / `prompts` /\n  `resources` arrays directly on this object.\n\nA `_connectionId` on the parent import selects bring-your-own-key (BYOK)\ncredentials. Without one, built-in providers run on platform-managed\ncredentials; catalog providers have no platform-managed credentials, so they\nsave without a connection but cannot run until one is attached.\n","required":["provider"],"properties":{"provider":{"type":"string","enum":["openai","gemini","anthropic","mistral","xai","huggingface","deepseek","cohere","groq"],"description":"AI provider to use."},"model":{"type":"string","description":"Model identifier for a catalog provider (generic flat format) — for example\n`grok-4.6`. Built-in providers set the model inside their own block\n(`openai.model`, `litellm.model`) instead."},"modelOptions":{"type":"object","description":"Model tuning for a catalog provider (generic flat format). Which keys, values, and\nranges are accepted is defined per catalog model and validated on save when the\nmodel is a catalog entry.","properties":{"maxOutputTokens":{"type":"number","description":"Maximum number of tokens the model may generate."},"temperature":{"type":"number","description":"Sampling temperature."},"topP":{"type":"number","description":"Nucleus sampling threshold."},"reasoning_effort":{"type":"string","enum":["low","medium","high","xhigh"],"description":"Reasoning depth for models that expose it."}}},"instructions":{"type":"string","description":"System prompt for a catalog provider (generic flat format). Built-in providers\nset it inside their own block (`openai.instructions`,\n`litellm._overrides.anthropic.systemInstruction`)."},"output":{"type":"object","description":"Output format for a catalog provider (generic flat format). Built-in providers\nconfigure it inside their own block (`openai.output`, `litellm.responseFormat`).","properties":{"format":{"type":"object","description":"Controls the structure of the model's output.","properties":{"type":{"type":"string","enum":["text","json_schema"],"description":"Output type."},"name":{"type":"string","description":"Name of the JSON schema, for `json_schema` output."},"strict":{"type":"boolean","description":"When true, the model must conform exactly to `jsonSchema`."},"jsonSchema":{"type":"object","additionalProperties":true,"description":"JSON Schema the structured output must conform to, for `json_schema` output."}}},"verbose":{"type":"string","description":"Level of detail in the model's response, for models that expose it."}}},"tools":{"type":"array","description":"Tools available to a catalog-provider agent (generic flat format). Entries mirror\nthe built-in providers' tool entries: a Celigo Tool by `_toolId`, an MCP server by\n`_mcpConnectionId`, or a vendor web-search tool.","items":{"type":"object","properties":{"type":{"type":"string","enum":["tool","mcp","web_search"],"description":"Type of tool entry."},"tool":{"type":"object","description":"Reference to a Celigo Tool resource, used when type is \"tool\".","properties":{"_toolId":{"type":"string","format":"objectId","description":"The Celigo Tool to call."}}},"mcp":{"type":"object","description":"MCP server connection, used when type is \"mcp\".","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if omitted). Each entry\nis either a plain tool name or an object carrying display metadata.","items":{"type":["string","object"]}}}},"config":{"type":"object","additionalProperties":true,"description":"Tool-specific options for vendor-native tools (for example web-search settings)."}}}},"prompts":{"type":"array","description":"MCP prompt entries available to a catalog-provider agent (generic flat format).\nEach entry references one MCP connection and the prompt names allowed from it.","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server that exposes the prompts."},"allowedPrompts":{"type":"array","description":"Prompt names the agent may fetch from the server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"},"openai":{"type":"object","description":"OpenAI-specific configuration. Used when `provider` is \"openai\".\n","required":["model","instructions"],"properties":{"instructions":{"type":"string","maxLength":1000000,"description":"System prompt that defines the AI agent's behavior, goals, and constraints.\n"},"model":{"type":"string","description":"OpenAI model identifier. Open string (not an enum) — model names change frequently."},"reasoning":{"type":"object","description":"Controls depth of reasoning for complex tasks.","properties":{"effort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh"],"description":"How much reasoning effort the model should invest"},"summary":{"type":"string","enum":["concise","auto","detailed"],"description":"Level of detail in reasoning summaries"}}},"temperature":{"type":"number","minimum":0,"maximum":2,"description":"Sampling temperature. Higher values (e.g. 1.5) produce more creative output,\nlower values (e.g. 0.2) produce more focused and deterministic output.\n"},"topP":{"type":"number","minimum":0.1,"maximum":1,"description":"Nucleus sampling parameter"},"topLogprobs":{"type":"number","minimum":0,"maximum":20,"description":"Number of most likely tokens to return log probabilities for at each output position."},"maxOutputTokens":{"type":"number","minimum":100,"maximum":128000,"default":5000,"description":"Maximum number of tokens in the model's response (server default observed live on create)"},"serviceTier":{"type":"string","enum":["auto","default","priority"],"default":"default","description":"OpenAI service tier. \"priority\" provides higher rate limits and\nlower latency at increased cost."},"output":{"type":"object","description":"Output format configuration","properties":{"format":{"type":"object","description":"Controls the structure of the model's output.\n","properties":{"type":{"type":"string","enum":["text","json_schema","blob"],"default":"text","description":"Output format type."},"schemaMode":{"type":"string","enum":["manual","json"],"description":"How the structured-output schema was authored in the UI.\nEditor state only — it does not change how `jsonSchema` is\nsent to the provider."},"name":{"type":"string","description":"Name for the output format (used with json_schema)"},"strict":{"type":"boolean","default":false,"description":"When true, enforces strict schema validation on output."},"jsonSchema":{"type":"object","description":"JSON Schema for structured output. Required when `format.type` is \"json_schema\".\n","properties":{"type":{"type":"string","description":"Root JSON Schema type of the structured output; use \"object\" for record-shaped results.","enum":["object","array","string","number","integer","boolean"]},"properties":{"type":"object","additionalProperties":true,"description":"JSON Schema definitions for each field the structured output may contain."},"required":{"type":"array","description":"Property names the model must include in the structured output.","items":{"type":"string"}},"additionalproperties":{"type":"boolean","description":"When true, the structured output may include properties beyond those defined in `properties`."}}}},"if":{"properties":{"type":{"const":"json_schema"}},"required":["type"]},"then":{"required":["name","jsonSchema"]}},"verbose":{"type":"string","enum":["low","medium","high"],"default":"medium","description":"Level of detail in the model's response"}}},"tools":{"type":"array","description":"Tools available to the AI agent during processing.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["web_search","mcp","image_generation","tool"],"description":"Type of tool."},"webSearch":{"type":"object","description":"Web search configuration (empty object to enable)"},"imageGeneration":{"type":"object","description":"Image generation configuration","properties":{"background":{"type":"string","description":"Controls whether generated images have a transparent or opaque background; use transparent only with output formats that support it (png, webp).","enum":["transparent","opaque"]},"quality":{"type":"string","description":"Rendering quality of generated images, trading detail for generation speed and file size.","enum":["low","medium","high"]},"size":{"type":"string","description":"Pixel dimensions of generated images; choose square, portrait, or landscape to match the intended use.","enum":["1024x1024","1024x1536","1536x1024"]},"outputFormat":{"type":"string","description":"File format of generated images; use png or webp when transparency is needed.","enum":["png","webp","jpeg"]}}},"mcp":{"type":"object","description":"MCP server tool configuration","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server"},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if\nomitted). Each entry is either a plain tool name (legacy\nform) or an object carrying display metadata.","items":{"type":["string","object"],"properties":{"name":{"type":"string","maxLength":256,"description":"Tool name as exposed by the MCP server."},"title":{"type":"string","maxLength":300,"description":"Display title shown for the tool."},"description":{"type":"string","maxLength":1000,"description":"Display description shown for the tool."}},"required":["name"]}},"allowedPrompts":{"type":"array","description":"Specific prompts to allow from the MCP server (used for MCP prompt entries; all if omitted).","items":{"type":"string"}}}},"tool":{"type":"object","description":"Reference to a Celigo Tool resource.\n","properties":{"_toolId":{"type":"string","format":"objectId","description":"Reference to the Tool resource"},"overrides":{"type":"object","description":"Per-agent overrides for the tool's internal resources","properties":{"connections":{"type":"array","description":"Remaps the tool's abstract connections for this agent. Each entry pairs\nthe tool's abstract connection placeholder (`_abstractId`) with the\nconcrete connection (`_id`) to use for this agent; entries without\n`_id` keep the tool's own default connection.\n","items":{"type":["object","null"],"required":["_abstractId"],"properties":{"_abstractId":{"type":"string","format":"objectId","description":"The tool's abstract connection placeholder being overridden."},"_id":{"type":"string","format":"objectId","description":"Concrete connection to use in place of the abstract placeholder."}}}}}}}}}}},"prompts":{"type":"array","description":"MCP prompt entries available to the agent. Each item references one MCP connection\nand the prompt names allowed from it. Configured alongside `tools` in the form but\nstored separately; an entry's `allowedPrompts` is what distinguishes a prompt entry\nfrom an MCP tool entry (which carries `allowedTools`).\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","description":"MCP server prompt configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedPrompts":{"type":"array","description":"Prompt names to allow from the MCP server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"}}},"litellm":{"type":"object","description":"LiteLLM proxy configuration. Used when `provider` is \"gemini\" or \"anthropic\".\n\nLiteLLM provides a unified interface to multiple AI providers. Gemini-specific\nsettings are in `_overrides.gemini`; Claude-specific settings are in\n`_overrides.anthropic`.\n\n`model` is required when litellm is the active provider path.\n","properties":{"model":{"type":"string","description":"LiteLLM model identifier. For Gemini, models are stored without the `gemini/`\nprefix; for Anthropic, use the Claude model id (e.g. `claude-sonnet-4-6`)."},"temperature":{"type":"number","minimum":0,"maximum":2,"description":"Sampling temperature"},"maxCompletionTokens":{"type":"number","minimum":100,"maximum":128000,"default":5000,"description":"Maximum number of tokens in the response"},"topP":{"type":"number","minimum":0.1,"maximum":1,"description":"Nucleus sampling parameter"},"seed":{"type":"number","description":"Random seed for reproducible outputs"},"responseFormat":{"type":"object","description":"Output format configuration","properties":{"type":{"type":"string","description":"Output format type.","enum":["text","json_schema","blob"],"default":"text"},"schemaMode":{"type":"string","enum":["manual","json"],"description":"How the structured-output schema was authored in the UI. Editor\nstate only — it does not change how `jsonSchema` is sent to the\nprovider."},"name":{"type":"string","description":"Name for the output format (used with json_schema)."},"strict":{"type":"boolean","description":"When true, enforces strict schema validation on output.","default":false},"jsonSchema":{"type":"object","description":"JSON Schema for structured output. Required when `responseFormat.type` is \"json_schema\".","properties":{"type":{"type":"string","description":"Root JSON Schema type of the structured output; use \"object\" for record-shaped results.","enum":["object","array","string","number","integer","boolean"]},"properties":{"type":"object","additionalProperties":true,"description":"JSON Schema definitions for each field the structured output may contain."},"required":{"type":"array","description":"Property names the model must include in the structured output.","items":{"type":"string"}},"additionalProperties":{"type":"boolean","description":"When true, the structured output may include properties beyond those defined in `properties`."}}}},"if":{"properties":{"type":{"const":"json_schema"}},"required":["type"]},"then":{"required":["name","jsonSchema"]}},"_overrides":{"type":"object","description":"Provider-specific overrides","properties":{"gemini":{"type":"object","description":"Gemini-specific configuration overrides.\n","required":["systemInstruction"],"properties":{"systemInstruction":{"type":"string","maxLength":1000000,"description":"System instruction for Gemini models. Equivalent to OpenAI's `instructions`.\nMaximum 1,000,000 characters.\n"},"tools":{"type":"array","description":"Gemini-specific tools","items":{"type":"object","properties":{"type":{"type":"string","enum":["googleSearch","urlContext","fileSearch","mcp","tool"],"description":"Type of Gemini tool."},"googleSearch":{"type":"object","description":"Google Search configuration (empty object to enable)"},"urlContext":{"type":"object","description":"URL context configuration (empty object to enable)"},"fileSearch":{"type":"object","description":"File search configuration, used when type is \"fileSearch\".","properties":{"fileSearchStoreNames":{"type":"array","description":"Names of the file search stores the model can query.","items":{"type":"string"}}}},"mcp":{"type":"object","description":"MCP server tool configuration, used when type is \"mcp\".","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if\nomitted). Each entry is either a plain tool name\n(legacy form) or an object carrying display metadata\n— same contract as the OpenAI `allowedTools`.","items":{"type":["string","object"],"properties":{"name":{"type":"string","maxLength":256,"description":"Tool name as exposed by the MCP server."},"title":{"type":"string","maxLength":300,"description":"Display title shown for the tool."},"description":{"type":"string","maxLength":1000,"description":"Display description shown for the tool."}},"required":["name"]}},"allowedPrompts":{"type":"array","description":"Specific prompts to allow from the MCP server (used for MCP prompt entries; all if omitted).","items":{"type":"string"}}}},"tool":{"type":"object","description":"Reference to a Celigo Tool resource, used when type is \"tool\".","properties":{"_toolId":{"type":"string","format":"objectId","description":"Reference to the Tool resource."},"overrides":{"type":"object","description":"Per-agent overrides for the tool's internal resources.","properties":{"connections":{"type":"array","description":"Remaps the tool's abstract connections for this agent. Each entry\npairs the tool's abstract connection placeholder (`_abstractId`)\nwith the concrete connection (`_id`) to use for this agent;\nentries without `_id` keep the tool's own default connection.\n","items":{"type":["object","null"],"required":["_abstractId"],"properties":{"_abstractId":{"type":"string","format":"objectId","description":"The tool's abstract connection placeholder being overridden."},"_id":{"type":"string","format":"objectId","description":"Concrete connection to use in place of the abstract placeholder."}}}}}}}}}}},"prompts":{"type":"array","description":"MCP prompt entries available to the Gemini agent. Each item references one\nMCP connection and the prompt names allowed from it. The presence of\n`allowedPrompts` distinguishes a prompt entry from an MCP tool entry.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","description":"MCP server prompt configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedPrompts":{"type":"array","description":"Prompt names to allow from the MCP server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"},"responseModalities":{"type":"array","description":"Response output modalities","items":{"type":"string","enum":["text","image"]},"default":["text"]},"topK":{"type":"number","description":"Top-K sampling parameter for Gemini"},"thinkingConfig":{"type":"object","description":"Controls Gemini's extended thinking capabilities","properties":{"includeThoughts":{"type":"boolean","description":"When true, includes the model's thinking steps in the response."},"thinkingBudget":{"type":"number","minimum":100,"maximum":4000,"description":"Maximum tokens allocated for thinking"},"thinkingLevel":{"type":"string","description":"Controls how much thinking effort the model applies; use higher levels for complex, multi-step tasks at the cost of latency and tokens.","enum":["minimal","low","medium","high"]}}},"imageConfig":{"type":"object","description":"Gemini image generation configuration","properties":{"aspectRatio":{"type":"string","description":"Aspect ratio of generated images; choose a ratio matching the intended display format.","enum":["1:1","2:3","3:2","3:4","4:3","4:5","5:4","9:16","16:9","21:9"]},"imageSize":{"type":"string","description":"Output resolution of generated images; higher resolutions increase detail and file size.","enum":["1K","2K","4K"]}}},"mediaResolution":{"type":"string","enum":["low","medium","high"],"description":"Resolution for media inputs (images, video)"}}},"anthropic":{"type":"object","description":"Claude-specific configuration overrides. Used when `provider` is \"anthropic\".\n","required":["systemInstruction"],"properties":{"systemInstruction":{"type":"string","maxLength":1000000,"description":"System instruction for Claude models. Equivalent to OpenAI's `instructions`.\nMaximum 1,000,000 characters.\n"},"topK":{"type":"number","minimum":0,"description":"Top-K sampling parameter. Deprecated on Claude models released after Claude Opus\n4.6, which reject any value at runtime; set it only on older models."},"thinkingConfig":{"type":"object","description":"Controls Claude's extended thinking capabilities.","properties":{"type":{"type":"string","enum":["enabled","disabled","adaptive"],"default":"disabled","description":"Extended thinking mode."},"budgetTokens":{"type":"number","minimum":1024,"description":"Maximum tokens allocated for thinking. Required when `type` is \"enabled\" —\nomitting it fails the save with 422 `invalid_thinking_config`."},"display":{"type":"string","enum":["summarized","omitted"],"default":"summarized","description":"How thinking output is surfaced in the response."},"effort":{"type":"string","enum":["low","medium","high","xhigh","max"],"description":"How much thinking effort the model applies; use with `type` \"adaptive\". Higher\nvalues (`xhigh`, `max`) may be gated to specific Claude models by the provider."}},"if":{"properties":{"type":{"const":"enabled"}},"required":["type"]},"then":{"required":["budgetTokens"]}},"serviceTier":{"type":"string","enum":["auto","standard_only"],"default":"auto","description":"Anthropic service tier for the request."},"tools":{"type":"array","description":"Claude-specific tools.","items":{"type":"object","properties":{"type":{"type":"string","enum":["tool","mcp","webSearch"],"description":"Type of Claude tool."},"tool":{"type":"object","description":"Reference to a Celigo Tool resource, used when type is \"tool\".","properties":{"_toolId":{"type":"string","format":"objectId","description":"Reference to the Tool resource."},"overrides":{"type":"object","description":"Per-agent overrides for the tool's internal resources.","properties":{"connections":{"type":"array","description":"Remaps the tool's abstract connections for this agent. Each entry\npairs the tool's abstract connection placeholder (`_abstractId`)\nwith the concrete connection (`_id`) to use for this agent;\nentries without `_id` keep the tool's own default connection.\n","items":{"type":["object","null"],"required":["_abstractId"],"properties":{"_abstractId":{"type":"string","format":"objectId","description":"The tool's abstract connection placeholder being overridden."},"_id":{"type":"string","format":"objectId","description":"Concrete connection to use in place of the abstract placeholder."}}}}}}}},"mcp":{"type":"object","description":"MCP server tool configuration, used when type is \"mcp\".","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if omitted).","items":{"type":"string"}}}},"webSearch":{"type":"object","description":"Web search configuration, used when type is \"webSearch\".","properties":{"version":{"type":"string","pattern":"^\\d{8}$","description":"Anthropic web search tool version (YYYYMMDD). Selects the tool version sent\non the wire; unsupported versions surface as an Anthropic 400."},"allowedDomains":{"type":"array","description":"Domains the search may return results from. Mutually exclusive with `blockedDomains`.","items":{"type":"string"}},"blockedDomains":{"type":"array","description":"Domains to exclude from search results. Mutually exclusive with `allowedDomains`.","items":{"type":"string"}},"userLocation":{"type":"object","description":"Approximate user location used to localize search results. When present, at\nleast one of `city`, `country`, `region`, or `timezone` must be set.","properties":{"type":{"type":"string","enum":["approximate"],"description":"Location type. Always \"approximate\"."},"city":{"type":"string","maxLength":256,"description":"City name for localizing search results."},"country":{"type":"string","maxLength":8,"description":"ISO 3166-1 alpha-2 country code for localizing search results."},"region":{"type":"string","maxLength":256,"description":"Region or state for localizing search results."},"timezone":{"type":"string","maxLength":64,"description":"IANA timezone for localizing search results."}}}}}}}},"prompts":{"type":"array","description":"MCP prompt entries available to the Claude agent. Each item references one MCP\nconnection and the prompt names allowed from it. The presence of `allowedPrompts`\ndistinguishes a prompt entry from an MCP tool entry.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","description":"MCP server prompt configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedPrompts":{"type":"array","description":"Prompt names to allow from the MCP server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"}}}}}}}},"if":{"properties":{"provider":{"const":"openai"}},"required":["provider"]},"then":{"required":["openai"],"properties":{"openai":{"required":["model","instructions"]}}},"else":{"if":{"properties":{"provider":{"enum":["gemini","anthropic"]}},"required":["provider"]},"then":{"required":["litellm"],"properties":{"litellm":{"required":["model"]}}},"else":{"if":{"properties":{"provider":{"enum":["mistral","xai","huggingface","deepseek","cohere","groq"]}},"required":["provider"]},"then":{"required":["model"]}}}},"McpResources":{"type":"array","description":"Governed MCP resources — read-only reference content (policies, schemas, documentation)\npulled from connected MCP servers and made available to the agent as a consistent source\nof truth. Each entry references one MCP connection and the specific resources allowed from it.","items":{"type":"object","required":["type","mcp"],"properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of resource entry. Always \"mcp\"."},"mcp":{"type":"object","required":["_mcpConnectionId","allowedResources"],"description":"MCP server resource configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server that exposes the resources."},"allowedResources":{"type":"array","minItems":1,"description":"Resources to expose to the agent from the MCP server. Must contain at least one\nentry; each entry identifies one resource by name and URI.","items":{"type":"object","required":["name","uri"],"properties":{"name":{"type":"string","description":"Display name of the MCP resource."},"uri":{"type":"string","description":"URI that identifies the resource on the MCP server."}}}}}}}}},"GuardrailConfig":{"type":"object","description":"Configuration for GuardrailImport adaptor type.\n\nGuardrails evaluate data flowing through integrations for safety and\ncompliance. The `type` field selects which check to apply, and the\ncorresponding sub-object (`aiAgent`, `pii`, or `moderation`) provides\nthe configuration.\n\nA `_connectionId` on the parent import is only needed for BYOK\n`ai_agent` guardrails. In responses the server echoes the active type's\nsub-object and applies the `confidenceThreshold` default; it also\nreturns inactive sibling sub-objects (e.g. `moderation: {categories: []}`\non a `pii` guardrail, or a populated `pii` left over from a type switch),\nbut only the active type's sub-object is meaningful. Legacy documents\nmay carry a server-written default `aiAgent` stub on `pii`/`moderation`\nguardrails; current servers strip the inactive `aiAgent` on write.","properties":{"type":{"type":"string","enum":["ai_agent","pii","moderation"],"description":"The type of guardrail to apply. Each type requires its corresponding\nsub-configuration object (`aiAgent`, `pii`, or `moderation`)."},"confidenceThreshold":{"type":"number","minimum":0,"maximum":1,"default":0.7,"description":"Confidence threshold (0 to 1). Detections below this threshold are\nignored. Lower values catch more issues but increase false positives."},"aiAgent":{"type":"object","description":"AI agent check configuration; set when `type` is `ai_agent`. On\n`pii`/`moderation` guardrails a legacy server-written stub may\nappear here — it is inert, and current servers strip it on write."},"pii":{"type":"object","required":["entities"],"description":"PII detection configuration. Required when `type` is `pii`.","properties":{"entities":{"type":"array","description":"PII entity types to detect. When `type` is `pii`, at least one\nentry is required; the inactive sibling on other guardrail types\nmay be served with an empty list.","items":{"type":"string","enum":["credit_card_number","card_security_code_cvv_cvc","cryptocurrency_wallet_address","date_and_time","email_address","iban_code","bic_swift_bank_identifier_code","ip_address","location","medical_license_number","national_registration_number","persons_name","phone_number","url","us_bank_account_number","us_drivers_license","us_itin","us_passport_number","us_social_security_number","uk_nhs_number","uk_national_insurance_number","spanish_nif","spanish_nie","italian_fiscal_code","italian_drivers_license","italian_vat_code","italian_passport","italian_identity_card","polish_pesel","finnish_personal_identity_code","singapore_nric_fin","singapore_uen","australian_abn","australian_acn","australian_tfn","australian_medicare","indian_pan","indian_aadhaar","indian_vehicle_registration","indian_voter_id","indian_passport","korean_resident_registration_number"]}},"mask":{"type":"boolean","default":false,"description":"When true, detected PII is replaced with masked values.\nWhen false, PII is flagged without modification."}}},"moderation":{"type":"object","required":["categories"],"description":"Content moderation configuration. Required when `type` is `moderation`.","properties":{"categories":{"type":"array","description":"Content moderation categories to check. When `type` is\n`moderation`, at least one entry is required; the inactive\nsibling on other guardrail types may be served with an empty\nlist.","items":{"type":"string","enum":["sexual","sexual_minors","hate","hate_threatening","harassment","harassment_threatening","self_harm","self_harm_intent","self_harm_instructions","violence","violence_graphic","illicit","illicit_violent"]}}}}},"required":["type","confidenceThreshold"],"if":{"required":["type"],"properties":{"type":{"const":"pii"}}},"then":{"required":["pii"],"properties":{"pii":{"required":["entities"],"properties":{"entities":{"minItems":1}}}}},"else":{"if":{"required":["type"],"properties":{"type":{"const":"moderation"}}},"then":{"required":["moderation"],"properties":{"moderation":{"required":["categories"],"properties":{"categories":{"minItems":1}}}}},"else":{"required":["aiAgent"],"properties":{"aiAgent":{"$ref":"#/components/schemas/AiAgentConfig"}}}}},"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"},"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\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**: Using wildcards like `*` and `?`\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- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"isempty\"`: Field is empty or null (is empty)\n- `\"isnotempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches specified pattern (matches)\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- Returns the raw field value which may need type conversion\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  - `export.settingName`: Access export-level settings\n  - `import.settingName`: Access import-level settings\n  - `integration.settingName`: Access integration-level settings\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\nType conversion operators are often necessary when comparing extracted field values against literals or when the field type doesn't match the comparison operator's expected type. 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    \"extract\",\n    \"status\"\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"extract\",\n    \"filename\"\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\n```json\n[\n  \"greaterthan\",\n  [\n    \"extract\",\n    \"orderDate\"\n  ],\n  \"2023-01-01T00:00:00Z\"\n]\n```\n\nExample 5: Category contains any of [\"Urgent\", \"High Priority\"]\n```json\n[\n  \"anyof\",\n  [\n    \"extract\",\n    \"category\"\n  ],\n  [\"Urgent\", \"High Priority\"]\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 that exists in the system.\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"}}}}},"Hook":{"type":"object","description":"A single lifecycle hook — a JavaScript function invoked from a script (or hosted on a\nstack) at a fixed point in an import or AI-agent run. Shared by every hook slot; the\nslot's own description says when in the lifecycle it fires.","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","null"],"description":"Static parameters passed to the hook function at runtime, letting one script be\nreused with different settings. Null when the hook carries no static parameters."}}},"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.\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"}}}}},"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, arrays of objects, arrays of arrays of objects) are ALL\n   handled through this recursive pattern\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","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- For arrays within arrays: Leave EMPTY for the inner array mappings, as they don't have field names\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, empty 'generate' fields almost always indicate inner array structures\nwithin a parent array.\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, attempts conversion from strings\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. REQUIRES child mappings in the 'mappings' array\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- `arrayarray`: Array of arrays (for matrix/table structures)\n\nArray dataTypes can be populated two ways: pass a source array through\nunchanged via `extract` alone (when the source is already an array of\nthe right shape), or construct/iterate via `buildArrayHelper`.\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` is one option (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","arrayarray"]},"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\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 `object`, `objectarray`, and primitive-array dataTypes as\n  well as scalars — the rendered template output must parse into the\n  declared shape\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- When both extract and mappings are defined for objects, extract is applied first\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 empty (the UI's \"Do nothing\" action).\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.\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"},"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, date)\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 REQUIRED mechanism for ALL array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.)\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**For Arrays of Arrays (arrayarray)**\n- Similar to objectarray, but inner arrays have empty 'generate' fields\n- Used for matrix/table structures\n```json\n{\n  \"generate\": \"matrix\",\n  \"dataType\": \"arrayarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.rows[*]\",  // For each row in the rows array\n      \"mappings\": [\n        {\n          \"dataType\": \"numberarray\",  // Note: No generate field for inner arrays\n          \"buildArrayHelper\": [\n            {\"extract\": \"$.rows.columns[*]\"}  // Notice: \"rows\" is singular in the composite object\n          ]\n        }\n      ]\n    }\n  ]\n}\n```\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"},"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":"Specifies the condition that must be met for an array element to be included.\n\n'extract_not_empty' only includes elements where the extract field returns a value.\n","enum":["extract_not_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"}}}}},"status":{"type":"string","description":"**Purpose**\nRequired on every mapping entry. Controls whether the mapping is applied.\n\n**Guidance**\nEmit `\"Active\"` for mappings that should run; `\"Draft\"` saves an\nin-progress mapping without the Active-only field validations. The\nAPI rejects a mapping missing this field\n(validation error: \"Mapping object must have status field present.\").\n","enum":["Active","Draft"]},"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**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":"Specifies the condition that triggers application of this mapping:\n- record_created: Apply only when creating a new record\n- record_updated: Apply only when updating an existing record\n- extract_not_empty: Apply only when the extract field returns a value\n","enum":["record_created","record_updated","extract_not_empty"]}}}},"required":["dataType"]}},"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- For arrays within arrays: Leave EMPTY for the inner array mappings, as they don't have field names\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, empty 'generate' fields almost always indicate inner array structures\nwithin a parent array.\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, attempts conversion from strings\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. REQUIRES child mappings in the 'mappings' array\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- `arrayarray`: Array of arrays (for matrix/table structures)\n\nArray dataTypes can be populated two ways: pass a source array through\nunchanged via `extract` alone (when the source is already an array of\nthe right shape), or construct/iterate via `buildArrayHelper`.\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` is one option (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","arrayarray"]},"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\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 `object`, `objectarray`, and primitive-array dataTypes as\n  well as scalars — the rendered template output must parse into the\n  declared shape\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- When both extract and mappings are defined for objects, extract is applied first\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 empty (the UI's \"Do nothing\" action).\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.\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"},"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, date)\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 REQUIRED mechanism for ALL array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.)\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**For Arrays of Arrays (arrayarray)**\n- Similar to objectarray, but inner arrays have empty 'generate' fields\n- Used for matrix/table structures\n```json\n{\n  \"generate\": \"matrix\",\n  \"dataType\": \"arrayarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.rows[*]\",  // For each row in the rows array\n      \"mappings\": [\n        {\n          \"dataType\": \"numberarray\",  // Note: No generate field for inner arrays\n          \"buildArrayHelper\": [\n            {\"extract\": \"$.rows.columns[*]\"}  // Notice: \"rows\" is singular in the composite object\n          ]\n        }\n      ]\n    }\n  ]\n}\n```\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"},"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":"Specifies the condition that must be met for an array element to be included.\n\n'extract_not_empty' only includes elements where the extract field returns a value.\n","enum":["extract_not_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"}}}}},"status":{"type":"string","description":"**Purpose**\nRequired on every mapping entry. Controls whether the mapping is applied.\n\n**Guidance**\nEmit `\"Active\"` for mappings that should run; `\"Draft\"` saves an\nin-progress mapping without the Active-only field validations. The\nAPI rejects a mapping missing this field\n(validation error: \"Mapping object must have status field present.\").\n","enum":["Active","Draft"]},"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**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":"Specifies the condition that triggers application of this mapping:\n- record_created: Apply only when creating a new record\n- record_updated: Apply only when updating an existing record\n- extract_not_empty: Apply only when the extract field returns a value\n","enum":["record_created","record_updated","extract_not_empty"]}}}},"required":["dataType"]},"MappingField":{"type":"object","description":"One Mapper 1.0 field-mapping entry.","properties":{"generate":{"type":"string","description":"Target field path to write on the destination record."},"extract":{"type":"string","description":"Source expression. Accepts:\n- JSONPath starting with `$.` (e.g. `$.customer.firstName`).\n- Handlebars template (contains `{{`) for concatenation /\n  formatting / conditional logic (e.g.\n  `{{record.firstName}} {{record.lastName}}`).\n- A literal string (any value not starting with `$.` and not\n  containing `{{`) — treated as a hardcoded value.\n\nOmit when using `hardCodedValue`.\n"},"hardCodedValue":{"type":["string","null"],"description":"Static value written to `generate` instead of extracting from the source record."},"dataType":{"type":"string","enum":["string","number","boolean","numberarray","stringarray","json"],"description":"Data type coercion applied to the mapped value."},"discardIfEmpty":{"type":"boolean","description":"When true, the field is omitted from the output when the extracted value is empty."},"immutable":{"type":"boolean","description":"When true, the mapped value cannot be overwritten by later mapping steps."},"lookupName":{"type":"string","description":"Name of an entry in the import's `lookups` array used to translate the value."},"default":{"type":["string","null"],"description":"Fallback value used when the extract yields no value."},"extractDateFormat":{"type":"string","description":"Date format of the source value, used to parse it before conversion."},"extractDateTimezone":{"type":"string","description":"Timezone applied when parsing the source date value."},"generateDateFormat":{"type":"string","description":"Date format applied to the value written to the destination."},"generateDateTimezone":{"type":"string","description":"Timezone applied when formatting the destination date value."},"conditional":{"type":"object","description":"Only apply this mapping entry when the given condition is satisfied.\n","properties":{"when":{"type":"string","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","expression"],"description":"Condition that gates whether this mapping entry is applied.\n`lookup_not_empty` / `lookup_empty` evaluate the lookup named by the\nsibling `lookupName`; `expression` evaluates the sibling `expression`.\n"},"lookupName":{"type":"string","description":"Lookup to evaluate for the `lookup_not_empty` / `lookup_empty`\nconditions.\n"},"expression":{"type":"string","description":"Expression evaluated when `when` is `expression`.\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.\nThis value typically matches the key in the fieldMap object.\n"},"name":{"type":"string","description":"Name of the field, used as the property name when generating the settings object\nfrom the submitted form data.\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.\n","default":false},"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 fields, specifies the placeholder and field name for the key input.\n"},"valueName":{"type":"string","description":"For keyvalue fields, specifies the placeholder and field name for the value input.\n"},"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.\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"]},"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.\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","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. This script must be accessible within the user's account\nand have appropriate permissions.\n"}}}}},"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"}}},"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},"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":"Reference to the integration app that defines this resource.\n\nThis field is only populated for resources that are part of an integration app.\nIt contains the unique identifier (_id) of the integration app (connector)\nthat defines the structure, behavior, and templates for this resource.\n\nThe integration app is the published template that can be installed\nmultiple times across different accounts, with each installation creating\na separate integration instance (referenced by _integrationId).\n\nThis reference enables:\n- Identifying the source integration app for this resource\n- Determining which template version is being used\n- Linking to documentation, support, and marketplace information\n"}}},"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 imports

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

````json
{"openapi":"3.2.0","info":{"title":"Imports","version":"1.0.0"},"tags":[{"name":"Imports","description":"Imports deliver data to destination systems. An import receives pages of records from\nupstream flow steps, applies field mappings and transformations, and writes the results\nto the target application, database, or file destination — handling lookups, duplicate\navoidance, and per-record error reporting along the way. Each import uses one\n`adaptorType` that determines its connection compatibility and configuration object.\n\n## Import schema\n\n{% openapi-schemas spec=\"import\" schemas=\"Import\" 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":{"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":{"Import":{"type":"object","required":["_id","name","adaptorType","apiIdentifier","createdAt","lastModified"],"description":"Import object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ImportBase"},{"$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 import."},"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the source resource this import was created from."},"_templateId":{"type":"string","format":"objectId","readOnly":true,"description":"Template this import was created from."},"draft":{"type":"boolean","readOnly":true,"description":"When true, this import 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 import expires."},"debugUntil":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp until which debug logging is enabled for this import."}}}]},"ImportBase":{"type":"object","description":"Writable import fields shared by the request and response schemas.","properties":{"_connectionId":{"type":"string","format":"objectId","description":"Connection this import uses to reach the destination system. The connection's type must\nbe compatible with the import's `adaptorType` (e.g. an `HTTPImport` needs an `http`\nconnection; `NetSuiteDistributedImport` and `NetSuiteHTTPImport` both need a `netsuite`\nconnection). Server-required — POST without it fails with 422 \"Expected field:\n_connectionId to be present\" — except for the connection-less flavors (`ToolImport`,\n`AiAgentImport`, `GuardrailImport`), which the server creates without one."},"_integrationId":{"type":["string","null"],"format":"objectId","description":"Integration this import belongs to."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this import was created from, set when the import is part of an installed integration app."},"adaptorType":{"type":"string","description":"Selects the adaptor technology that executes this import, which determines the compatible\nconnection types and which adaptor-specific configuration object must also be supplied\n(e.g. set `salesforce` when using `SalesforceImport`).","enum":["HTTPImport","FTPImport","AS2Import","S3Import","NetSuiteImport","NetSuiteDistributedImport","NetSuiteHTTPImport","SalesforceImport","JDBCImport","RDBMSImport","MongodbImport","DynamodbImport","WrapperImport","AiAgentImport","GuardrailImport","FileSystemImport","ToolImport","RESTImport"]},"nsDomainType":{"type":"string","enum":["suitetalk","restlet"],"description":"Selects which NetSuite REST host a `NetSuiteHTTPImport` 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 each\n`http.relativeURI` entry carries the complete path starting at `/services/rest/...` or\n`/app/site/hosting/restlet.nl`. Ignored on other adaptor types."},"externalId":{"type":["string","null"],"description":"External identifier for correlating the import with a record in another system."},"as2":{"$ref":"#/components/schemas/As2"},"dynamodb":{"$ref":"#/components/schemas/Dynamodb"},"http":{"$ref":"#/components/schemas/Http"},"ftp":{"$ref":"#/components/schemas/Ftp"},"jdbc":{"$ref":"#/components/schemas/Jdbc"},"mongodb":{"$ref":"#/components/schemas/Mongodb"},"netsuite":{"$ref":"#/components/schemas/NetSuite"},"netsuite_da":{"$ref":"#/components/schemas/NetsuiteDistributed"},"rdbms":{"$ref":"#/components/schemas/Rdbms"},"s3":{"$ref":"#/components/schemas/S3"},"wrapper":{"$ref":"#/components/schemas/Wrapper"},"salesforce":{"$ref":"#/components/schemas/Salesforce"},"tool":{"$ref":"#/components/schemas/Tool"},"file":{"$ref":"#/components/schemas/File"},"filesystem":{"$ref":"#/components/schemas/FileSystem"},"aiAgent":{"$ref":"#/components/schemas/AiAgentConfig"},"guardrail":{"$ref":"#/components/schemas/GuardrailConfig"},"name":{"type":"string","minLength":1,"maxLength":100,"description":"Display name for the import, shown in the flow builder, job history, and error logs.\nDescriptive, unique names indicating the destination system and purpose make large\naccounts easier to manage."},"description":{"type":["string","null"],"description":"Free-text summary of what the import writes and why. Shown in the UI and available to\nAI agents for context; has no effect on execution.","maxLength":5120},"unencrypted":{"type":"object","description":"Custom configuration values stored without encryption and returned in API responses."},"sampleData":{"type":["object","array","string"],"description":"Sample input record used to preview and build the import's mappings."},"distributed":{"type":"boolean","description":"When true, the import uses a distributed adaptor (such as `NetSuiteDistributedImport`)\nthat executes inside the target application rather than on Celigo's servers."},"maxAttempts":{"type":"number","description":"Maximum number of attempts made to deliver a record before it is marked as failed."},"ignoreExisting":{"type":"boolean","description":"When true, records that already exist in the destination system are silently skipped\ninstead of being created or updated — used for create-only operations that must avoid\nduplicates. Existing records are identified by the import's lookup configuration or by\na populated `ignoreExtract` field on the incoming record."},"ignoreMissing":{"type":"boolean","description":"When true, records that do not already exist in the destination system are silently\nskipped instead of producing errors — used for update-only operations."},"idLockTemplate":{"type":["string","null"],"description":"Handlebars template that generates a lock key for each record so records resolving to\nthe same key are not submitted concurrently, preventing duplicate or conflicting writes\nto the same target record."},"dataURITemplate":{"type":["string","null"],"description":"Handlebars template that builds a link back to each record in the destination\napplication's UI. The resolved URL is stored with error records in job history so users\ncan jump straight to the record."},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"blobKeyPath":{"type":"string","description":"Path in the input record that holds the blob key identifying the file content to import.\nAt send time the platform follows this path, retrieves the referenced file from integrator.io\nstorage, and streams its bytes into the outgoing request."},"blob":{"type":"boolean","description":"When true, this import transfers raw file content (blobs) to the destination rather than structured records."},"assistant":{"type":"string","description":"Identifier for the connector assistant used to configure this import."},"deleteAfterImport":{"type":"boolean","description":"When true, the source file is deleted after it is successfully imported."},"assistantMetadata":{"type":"object","description":"Metadata associated with the connector assistant configuration."},"useTechAdaptorForm":{"type":"boolean","description":"When true, the UI presents the full technical adaptor form for this import instead of\nthe simplified assistant form."},"distributedAdaptorData":{"type":"object","description":"Internal state stored by distributed adaptors (such as the NetSuite SuiteApp) for this import."},"filter":{"description":"Filter applied to incoming records before they are sent to the destination system.\nRecords that match continue through the import; records that don't are silently\ndropped. Filter expressions reference the incoming record's fields.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"traceKeyTemplate":{"type":"string","description":"Handlebars template that overrides how each record's unique trace key is generated,\nused to track records through the flow and match errors to records. Trace keys are\ncapped at 256 characters; a longer key is stored truncated from the middle, keeping\nthe beginning and end of the value."},"mockResponse":{"type":"array","description":"Predefined response records used in place of calling the destination system when\ntesting the import, so flows can run without writing real data. Records must be in\nintegrator.io canonical format — the server rejects any other shape with a 422.","items":{"type":"object","properties":{"statusCode":{"type":"integer","description":"HTTP status code the mock returns (e.g. 200, 403)."},"id":{"type":["string","integer"],"description":"Identifier echoed for the mocked record."},"ignored":{"type":"boolean","description":"When true, the mocked record reports as ignored rather than imported."},"dataURI":{"type":"string","description":"Data URI echoed for the mocked record."},"errors":{"type":"array","description":"Mock error entries returned with the record."},"_json":{"type":["object","array"],"description":"Mock response body."},"_headers":{"type":"object","description":"Mock response headers."}}}},"_ediProfileId":{"type":"string","format":"objectId","description":"EDI profile this import uses to generate outbound X12 or EDIFACT documents — it supplies\nthe envelope qualifiers, delimiters, version, and validation rules. Set it when the\nimport produces EDI output; omit it otherwise. Accepted on the file-writing imports —\nFTP, AS2, S3 and HTTP file mode (`http.type: \"file\"`)."},"parsers":{"type":"array","description":"Legacy parser configuration slot. The server initializes this field to an empty array\nand current API writes never populate it; treat it as server bookkeeping rather than a\nsetting to configure."},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the import lifecycle for\ntransformations, validation, and custom result handling beyond what configuration alone\ncan express.","properties":{"preMap":{"type":"object","description":"Hook that runs on each page of records before the import's mappings are applied.\nCommonly used to reshape or filter records ahead of mapping.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postMap":{"type":"object","description":"Hook that runs after the import's mappings are applied but before records are sent\nto the destination system. Commonly used for adjustments that need the mapped\n(destination-shaped) record.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postSubmit":{"type":"object","description":"Hook that runs after the destination system responds, with access to both the\nsubmitted records and the destination's responses. Commonly used to inspect results\nor adjust the response data passed downstream.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postAggregate":{"type":"object","description":"Hook that runs after an aggregated file has been submitted to the destination, for\nfile-based imports that combine records into a single file.","allOf":[{"$ref":"#/components/schemas/Hook"}]}}},"sampleResponseData":{"type":["object","array","string"],"description":"Sample response payload used to preview response mappings and test downstream steps\nwithout calling the destination system."},"responseTransform":{"description":"Transformation that reshapes the destination system's response after records are\nimported, before the response is processed by response mappings and downstream\nsteps. Commonly used to extract relevant fields from verbose API responses.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"modelMetadata":{"type":"object","description":"Metadata about the destination data model captured for this import. Rarely set."},"mapping":{"type":"object","description":"Mapper 1.0 mapping configuration. `fields` maps individual fields; `lists` maps\nrecord lists, each with its own `fields` array. Superseded by Mapper 2.0\n(`mappings`) but still widely used.","properties":{"fields":{"type":"array","description":"Field-level mapping entries applied to each record.","items":{"$ref":"#/components/schemas/MappingField"}},"lists":{"type":"array","description":"List-level mappings, each generating a sublist on the target record.","items":{"type":"object","properties":{"generate":{"type":"string","description":"Target sublist or array path to generate."},"fields":{"type":"array","description":"Field mappings applied within each generated list item.","items":{"$ref":"#/components/schemas/MappingField"}}}}}}},"mappings":{"description":"Field mappings that transform incoming records into the destination system's field\nstructure — renaming fields, converting types, building nested objects, and applying\nformulas or lookups to derive values.","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"lookups":{"description":"Top-level mirror of the adaptor-specific ``lookups`` array. Celigo persists lookups\nin TWO locations on every import resource: ``<adaptor_key>.lookups`` (the typed,\nadaptor-specific definition) and this top-level ``lookups`` array (a permissive copy).","allOf":[{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the shape of the input passed to the import's processing pipeline."},"settingsForm":{"$ref":"#/components/schemas/Form"},"preSave":{"$ref":"#/components/schemas/PreSave"},"settings":{"$ref":"#/components/schemas/Settings"}}},"As2":{"type":"object","description":"Configures how outbound AS2 messages are built for this import. Required when the\n_connectionId field references an AS2 connection. AS2 (Applicability Statement 2) transmits\nEDI and other data securely over HTTP/S using S/MIME encryption and digital signatures; this\nobject names the payload file, the message identifier, retry behavior, and any extra headers.","required":["fileNameTemplate"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies the partner-specific EDI and AS2 configuration.\nWhen set, the import inherits the connector's pre-configured settings; omit to use only\nthe AS2 connection details."},"fileNameTemplate":{"type":"string","description":"Handlebars template that names the file carrying each AS2 message payload. Include a\nuniqueness token such as {{timestamp}} so concurrent or repeated sends do not collide;\ndefaults to file-{{timestamp}} in the form."},"messageIdTemplate":{"type":"string","description":"Handlebars template that generates the Message-ID header for each AS2 message. Placeholders\nare replaced at runtime; the resulting value must be globally unique and follow RFC 5322\nheader formatting. Leave unset to let the platform generate the Message-ID."},"maxRetries":{"type":"number","default":0,"description":"Number of times a failed transmission is retried after transient errors such as network\nfailures or timeouts; does not affect the initial send. The form offers 1–5; set to 0\n(the default) to report failure immediately without retrying."},"headers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header sent with the AS2 message (for example, `content-disposition`). Header names are case-insensitive."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions rendered against the record."}}},"description":"HTTP headers sent with the AS2 message transmission.\nSupports both standard AS2 headers and custom headers required by trading partner agreements."}}},"Dynamodb":{"type":"object","description":"Configuration for DynamoDB imports. Required when the _connectionId field references a\nDynamoDB connection; must not be included for other connection types. putItem writes a full\nitem document; updateItem modifies named attributes of an existing item identified by its\nkey.","required":["region","method","tableName"],"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"],"default":"us-east-1","description":"AWS region hosting the DynamoDB table. Determines the service endpoint used for all\nrequests and must match the region where the table is deployed."},"method":{"type":"string","enum":["putItem","updateItem"],"description":"Write operation performed against the DynamoDB table for each record."},"tableName":{"type":"string","description":"DynamoDB table that receives the imported records.\nMust match an existing table in the target AWS account and region; table names are case-sensitive."},"partitionKey":{"type":"string","description":"Value of the target item's partition key for the updateItem operation; together with\nsortKey (when the table defines one) it identifies the item each record writes to.\nSupports handlebars to reference the incoming record's fields."},"sortKey":{"type":"string","description":"Value of the target item's sort key; supports handlebars. Omit when the table defines only\na partition key."},"itemDocument":{"type":"string","description":"JSON document written to the table for the putItem operation, containing all attribute\nnames and values for the item, including its key attributes. Supports handlebars."},"updateExpression":{"type":"string","description":"DynamoDB update expression naming which attributes to set, add, or remove on the item for\nthe updateItem operation. Only the named attributes change; reference attribute names and\nvalues through the expressionAttributeNames and expressionAttributeValues placeholders."},"conditionExpression":{"type":"string","description":"Condition evaluated against the existing item before the write executes.\nWhen the condition evaluates to false, the operation is aborted and no changes are made."},"expressionAttributeNames":{"type":"string","description":"JSON placeholder-to-name map for attribute names referenced in expressions, for example {\"#N\": \"Name\"}.\nUse it when an attribute name is a reserved word or contains special characters; placeholder keys start with #."},"expressionAttributeValues":{"type":"string","description":"JSON placeholder-to-value map for attribute values referenced in expressions; placeholder\nkeys start with a colon (e.g. \":val\") and each value is a DynamoDB attribute-value object\nsuch as {\"S\": \"text\"}. Supports handlebars."},"ignoreExtract":{"type":"string","description":"Path to the field in the source record used to determine whether the record already exists.\nOnly used when ignoreExisting is set on the import."}},"if":{"properties":{"method":{"const":"updateItem"}},"required":["method"]},"then":{"required":["partitionKey","updateExpression"]}},"Http":{"type":"object","description":"Configuration for HTTP imports.\nRequired whenever the connection referenced by _connectionId has type http, and on\n`NetSuiteHTTPImport` (NetSuite's REST APIs on a `netsuite` connection, where `nsDomainType`\nsupplies the host).","properties":{"sendPostMappedData":{"type":"boolean","default":true,"description":"When true, the request body is the record as reshaped by the import's mapping step. When false, the original pre-mapped record is sent instead, bypassing the mappings for the payload."},"isRest":{"type":"boolean","description":"Internal flag indicating the import was built with the legacy REST form rather than the unified HTTP framework. Set by the form; not configured directly."},"strictHandlebarEvaluation":{"type":"boolean","description":"When true, handlebars expressions that reference a missing field raise an error instead of rendering an empty string, surfacing mapping mistakes early rather than sending blank values."},"formType":{"type":"string","enum":["assistant","http","rest","graph_ql","assistant_graphql"],"description":"Determines the UI form and configuration experience for this import."},"type":{"type":"string","enum":["file","records"],"description":"Controls whether the import sends structured record data or raw file content.\nMost HTTP imports use records; use file only when importing raw file content that will be processed downstream."},"requestMediaType":{"type":"string","enum":["xml","json","csv","urlencoded","form-data","octet-stream","plaintext"],"description":"Content type used to serialize the request body sent to the target API.\nMost REST APIs use json."},"_httpConnectorEndpointIds":{"type":"array","readOnly":true,"items":{"type":"string","format":"objectId"},"description":"HTTP connector endpoint IDs used by this import (multiple endpoints for different request\ntypes or operations). Set by the connector framework; client-supplied values are ignored."},"blobFormat":{"type":"string","enum":["utf8","ucs2","utf-16le","ascii","binary","base64","hex"],"description":"Character encoding format for blob/binary data imports.\nOnly relevant when type is \"file\" or when handling binary content.\n"},"batchSize":{"type":"integer","description":"Maximum number of records submitted per HTTP request, which affects throughput and API rate limiting.\nREST services typically send one record per request; raise it only for batch endpoints or RPC/XML services that accept multiple records, consulting the target API documentation for the optimal value.\n\nThe default varies by API."},"successMediaType":{"type":"string","enum":["xml","json","plaintext"],"description":"Media type expected in successful responses from the target API. Most APIs return json."},"requestType":{"type":"array","items":{"type":"string","enum":["CREATE","UPDATE"]},"description":"Operation type each request performs; for composite (upsert) imports, include both values.\nThe array is positionally aligned with the relativeURI and method arrays — each index maps to one operation — and the existingExtract field determines which operation runs at runtime."},"errorMediaType":{"type":"string","enum":["xml","json","plaintext"],"description":"Media type expected in error responses from the target API. Most APIs return json."},"followRedirects":{"type":"boolean","default":true,"description":"When explicitly false, 3xx responses are not followed — the redirect\nresponse itself (status code, `Location` header, body) is what the\nimport records as the result. Omitted or true follows allowed\nredirects (the default behavior)."},"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":"array","items":{"type":"string"},"description":"Relative URI path for each import request, as an array of strings (never an object); values can include Handlebars expressions for dynamic segments.\nTemplates render against the pre-mapped record — the original input before the Import's mapping step — because URI construction usually needs business identifiers that mappings may rename or remove.\nFor composite (upsert) imports, the array is positionally aligned with method and requestType, and existingExtract decides the index used at runtime: the UPDATE index when its field has a value, the CREATE index when it is empty or missing.\nReference the existing record ID in the UPDATE URI with `{{{data.0.fieldName}}}` (triple braces with the data.0 prefix).\nUse separate array elements per operation rather than `{{#if}}` conditionals inside a single URI."},"method":{"type":"array","items":{"type":"string","enum":["GET","PUT","POST","PATCH","DELETE"]},"description":"HTTP method used for each import request.\nFor composite (upsert) imports, the array is positionally aligned with relativeURI and requestType — e.g. `[\"PUT\", \"POST\"]` with `requestType: [\"UPDATE\", \"CREATE\"]` uses PUT for updates and POST for creates."},"_httpConnectorVersionId":{"type":"string","format":"objectId","readOnly":true,"description":"HTTP connector version used by this import. Set by the connector framework; client-supplied values are ignored (write-tested)."},"_httpConnectorResourceId":{"type":"string","format":"objectId","readOnly":true,"description":"HTTP connector resource used by this import. Set by the connector framework; client-supplied values are ignored (write-tested)."},"_httpConnectorEndpointId":{"type":"string","format":"objectId","readOnly":true,"description":"HTTP connector endpoint used by this import (single endpoint). Set by the connector framework; client-supplied values are ignored (write-tested)."},"body":{"type":"array","items":{"type":["string","null"]},"description":"Request body template for the import — an array of Handlebars template strings\npositionally aligned with the `method` array (e.g. `[\"{{{record}}}\"]`). A `null`\nentry means no template for the method at that position. Each entry is a string:\nwrite a JSON body as a template string (`[\"{\\\"id\\\": \\\"{{record.id}}\\\"}\"]`), not as\na JSON object, because an object entry does not render as JSON at runtime.\nLeave undefined for standard imports — the Import's mapping step transforms the source record and the mapped data is sent as the request body automatically.\nSet it only when the target API requires XML, SOAP, or a custom structure that mappings cannot produce, or when the user explicitly specifies a request body.\nWhen set, the mapping step still runs first and the template renders against the post-mapped record — use `{{record.<mappedField>}}` to reference mapping outputs.\nHand-templated bodies are harder to maintain and debug than mappings; when in doubt, leave this field undefined.\n\nCustom envelopes can combine static header or account fields with per-message\nfield references inside a loop.\n\nStatic wrappers around the mapped record are appropriate for message-bus class envelopes,\nHMAC signature wrappers, integration batch headers,\nnamed single-record array wrappers, and similar destination-required structures.\n\nBatch templates can wrap an envelope, such as a header, version, or marketplace,\naround serialized records.\n\nUse this pattern when each message needs a small number of fields rather than the whole record."},"ignoreEmptyNodes":{"type":"boolean","description":"When true, fields with empty values are stripped from the request body before it is sent,\nso the target API receives only populated fields. Shown as \"Remove empty fields from HTTP\nrequest body\" in the UI."},"existingExtract":{"type":"string","description":"Field name or JSON path that drives the upsert decision for composite imports with both CREATE and UPDATE in requestType.\nWhen the field has a value in the incoming record, the UPDATE operation's relativeURI and method are used; when it is empty or missing, the CREATE operation runs.\nMust match a field populated by an upstream lookup or response mapping (e.g. a destination system ID like \"shopifyCustomerId\"); omit for single-operation imports."},"existingLookupName":{"type":"string","description":"Name of an entry in `lookups` that resolves whether each record already exists in the destination, driving the composite upsert decision when `existingExtract` alone is insufficient (e.g. the check requires a call to the target system). Paired with composite CREATE/UPDATE imports.\n\nUse `existingLookupName` when the incoming record has no destination ID and\nneeds a per-record probe to discover one;\nuse `existingExtract` when the record already carries the ID.\n\nFor single-operation imports,\nuse `ignoreExtract` or `ignoreLookupName` with `ignoreExisting` or\n`ignoreMissing` to skip records instead of routing them.\n\nThe lookup pattern adds one HTTP request per record at runtime,\nmaking it better suited to one-off migrations and low-volume reconciliation than\nhigh-volume recurring syncs."},"ignoreExtract":{"type":"string","description":"Field name on the incoming record that drives the SKIP-vs-PROCESS\ndecision when ``ignoreExisting`` or ``ignoreMissing`` is set.\n\nPairs with either of the two skip-mode flags:\n\n- With ``ignoreExisting: true`` (CREATE-only imports that should\n  avoid duplicates): if ``ignoreExtract`` has a value on the\n  record, the record is treated as already-existing and is\n  skipped; if empty, the record is created.\n- With ``ignoreMissing: true`` (UPDATE-only imports that should\n  avoid creating new records): if ``ignoreExtract`` has a value,\n  the record is treated as existing and is updated; if empty,\n  the record is skipped.\n\n``ignoreExtract`` checks a field that's already on the record —\nzero per-record HTTP overhead.  Use ``ignoreLookupName`` instead\nwhen the record has no field to inspect and the existence check\nneeds a per-record probe against the destination.\n\n**Mutually exclusive with ``ignoreLookupName``**\n\nSet EXACTLY ONE of ``ignoreExtract`` / ``ignoreLookupName`` when\n``ignoreExisting`` or ``ignoreMissing`` is enabled.\n\n**When not to set this field**\n\nOnly valid for single-operation imports with either\n``ignoreExisting: true`` or ``ignoreMissing: true`` set.\nComposite (upsert) imports — those with both ``CREATE`` and\n``UPDATE`` in ``requestType`` — use ``existingExtract`` /\n``existingLookupName`` instead, because they route records\nbetween two write paths rather than skipping them."},"endPointBodyLimit":{"type":"integer","description":"Maximum size limit for the request body in bytes.\nUsed to enforce API-specific size constraints.\n"},"headers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header to include with each import request."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions rendered against the pre-mapped record."}}},"description":"Custom HTTP headers included with import requests; values can contain Handlebars expressions.\nValue templates render against the pre-mapped record (the original input before the Import's mapping step) — use `{{record.<field>}}` to reference fields as they appear in the upstream source."},"response":{"type":"object","properties":{"resourcePath":{"type":"array","items":{"type":"string"},"description":"Per-operation JSON path to the resource collection in each\nresponse.  Outer-array length MUST match ``requestType``\nlength: 1 entry for single-op imports, N entries for an\nN-operation composite import (duplicate the value when\noperations agree).  See the ``response`` object docs\nabove for the full positional-array contract.\n\nRequired when ``batchSize > 1`` because the platform needs\nto know where the array of result records lives inside the\nresponse envelope.  When the API returns a bare array at\nthe top level, leave this field unset.\n\nEach entry is a DOT-PATH STRING (e.g. ``\"data.results\"``,\n``\"items\"``) — do NOT split paths into segments across\narray elements."},"resourceIdPath":{"type":"array","items":{"type":["string","null"]},"description":"Per-operation JSON path to the unique ID field within each\nrecord in the response.  Outer-array length MUST match\n``requestType`` length — duplicate the value when both\noperations agree (the common case) rather than collapsing\nto a 1-entry array.\n\nEach entry is a DOT-PATH STRING, or null for an operation\nwith no ID path configured. When not specified, the platform\nlooks for standard `id` or `_id` fields automatically."},"successPath":{"type":"array","items":{"type":"string"},"description":"Per-operation JSON path to a field that indicates whether\nthe API call succeeded.  Outer-array length MUST match\n``requestType`` length — duplicate the value when both\noperations agree rather than collapsing to a 1-entry\narray on a composite import.\n\nUse this when the API returns HTTP 200 for everything and\nsignals success / failure through a body field.  Pairs with\n``successValues`` to define which values at this path mean\nsuccess.\n\nEach entry is a DOT-PATH STRING."},"successValues":{"type":"array","items":{"type":"array","items":{"type":"string"}},"description":"Per-operation list of values at ``successPath`` that mean\nsuccess.  TWO levels of array nesting:\n\n- Outer array: one entry per ``requestType`` index.\n  Length MUST match ``requestType`` length — duplicate\n  the inner array when both operations agree rather than\n  collapsing to a 1-entry outer array on a composite\n  import.\n- Inner array: the list of acceptable success values for\n  that operation.\n\nAll comparisons are STRING comparisons regardless of the\nactual response value type — the platform stringifies\nnumbers, booleans, etc. before comparing."},"failPath":{"type":"array","items":{"type":"string"},"description":"Per-operation JSON path to a field that signals failure\neven when HTTP returns 200.  Outer-array length MUST\nmatch ``requestType`` length — duplicate when both\noperations agree.  Same array semantics as ``successPath``.\n\nSome APIs use only ``successPath`` (failure = absence of\nsuccess); others publish both an explicit success indicator\nand an explicit failure indicator.  When both are\navailable, set both — the platform evaluates ``successPath``\nfirst and falls through to ``failPath`` only when the\nsuccess check is inconclusive."},"failValues":{"type":"array","items":{"type":"array","items":{"type":"string"}},"description":"Per-operation list of values at ``failPath`` that mean\nfailure.  Same two-level array shape and length contract\nas ``successValues`` — outer length MUST match\n``requestType`` length, duplicate the inner array on\ncomposite imports when both operations agree.\n\nOften used as the inverse of ``successValues`` (e.g.\n``successValues: [[\"true\"]]`` paired with\n``failValues: [[\"false\"]]``)."},"errorPath":{"type":"string","description":"JSON path to the error message inside the response body.\n\n**Note**: this field is NOT a per-operation array — it's a\nsingle string applied to every operation.  Most APIs use\nthe same error-message field shape for both UPDATE and\nCREATE responses, so the platform doesn't expose a per-op\noverride here."},"allowArrayforSuccessPath":{"type":"boolean","description":"When true, allows array values at successPath during success evaluation."},"hasHeader":{"type":"boolean","description":"When true, treats the first record in the response as a header row (for CSV responses)."}},"description":"Configuration for parsing and interpreting HTTP responses returned\nfrom each write request the import issues.\n\n**Critical: positional per-operation arrays**\n\nEvery field inside ``response`` (``resourcePath``, ``resourceIdPath``,\n``successPath``, ``successValues``, ``failPath``, ``failValues``)\nis an ARRAY whose outer length is positionally aligned with\n``requestType`` / ``method`` / ``relativeURI``.  This is the\nSAME positional-alignment contract as those three sibling\narrays — see ``relativeURI`` docs for the full explanation.\n\nEach outer-array index describes how to parse the response from\nthe operation at that same index in ``requestType``:\n\n- **Single-operation import** (``requestType: [\"CREATE\"]``):\n  every response field is a 1-element array.\n  ``successPath: [\"ok\"]`` means \"for the one operation,\n  look at path ``ok`` to detect success\".\n- **Composite upsert** (``requestType: [\"UPDATE\", \"CREATE\"]``):\n  every response field is a 2-element array.  Index 0 parses\n  the UPDATE response, index 1 parses the CREATE response.\n\n**successValues / failValues — array of arrays**\n\n``successValues`` and ``failValues`` carry an extra array level\nbecause each operation can have MULTIPLE acceptable values:\n\n- Outer array: one entry per operation (positional, as above).\n- Inner array: the list of acceptable values for that operation.\n\nExample:\n```json\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"successPath\": [\"ok\", \"ok\"],\n\"successValues\": [[\"true\", \"1\"], [\"true\"]]\n```\nMeans: the UPDATE response is successful when ``ok`` is either\n``\"true\"`` or ``\"1\"``; the CREATE response is successful only\nwhen ``ok`` is ``\"true\"``.\n\n**Always duplicate when operations agree (REQUIRED for composite)**\n\nThe outer-array length MUST match ``requestType`` length on\nevery response field.  When the UPDATE and CREATE responses\nof the same connector look the same (which is the common\ncase — most APIs return the same shape from both endpoints),\nDUPLICATE the entry so the array has the same length as\n``requestType``.  Do NOT collapse to a 1-entry array on a\ncomposite import.\n\n```json\n// ✅ Correct — explicit per-operation entries, even when identical\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data.id\", \"data.id\"],\n\"successPath\": [\"success\", \"success\"],\n\"successValues\": [[\"true\"], [\"true\"]]\n```\n\n```json\n// ❌ Wrong — 1-entry array on a composite import\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data.id\"],\n\"successPath\": [\"success\"],\n\"successValues\": [[\"true\"]]\n```\n\nWhy duplicate even when they agree:\n\n- **UI parity.**  The Celigo UI renders one response-handling\n  form per ``requestType`` index.  A 1-entry array on a\n  composite import leaves the second operation's form panel\n  empty, which looks like a half-built config to the user\n  reviewing the saved import — even if it works at runtime.\n- **Symmetric shape rule.**  ``relativeURI`` / ``method`` /\n  ``requestType`` REQUIRE the entry count to match across\n  all three (the runtime decider uses positional alignment).\n  Making ``response.*`` follow the same \"outer length ==\n  requestType length\" rule means there's ONE shape rule to\n  remember, not two.\n- **Unambiguous intent.**  A 2-entry duplicated array\n  documents in the saved config that the developer\n  considered both operations.  A 1-entry array could mean\n  \"both ops use this\" or could mean \"I forgot to fill in\n  the second op.\"  Duplication eliminates the ambiguity.\n\nWhen operations DIFFER (rare — different response shapes\nper endpoint), set distinct values at each index.  Either\nway the outer length always matches ``requestType``.\n\n**Common llm mistake (DO not do THIS)**\n\nDo NOT split a single dot-path into array segments thinking the\nouter array represents path segments:\n\n```json\n// WRONG — the LLM expressed \"data.id\" as path segments\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data\", \"id\"]\n```\n\nThis is interpreted by the platform as: UPDATE response's id is\nat path ``data``, CREATE response's id is at path ``id`` — almost\nnever what the user wants.  The CORRECT shape is the single\ndot-path expressed as ONE string per operation, duplicated to\nmatch ``requestType`` length:\n\n```json\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data.id\", \"data.id\"]\n```"},"_asyncHelperId":{"type":"string","format":"objectId","description":"Reference to an AsyncHelper resource that polls an asynchronous\ndestination API on this import's behalf.\n\nSet this ONLY when the destination API is genuinely asynchronous — it\nacknowledges the submitted payload (HTTP 202, a job ticket) and ingests\nit in the background, so completion must be polled for (e.g. bulk\nimage/file ingestion, large bulk-load endpoints). Most imports are\nsynchronous and need NO async helper; adding one to a synchronous\ndestination just adds polling overhead plus a status and result export\nto maintain. When in doubt, leave it unset.\n\nThe referenced helper bundles the polling config plus a required status\nexport (polled to check progress) and a result export (fetches the\nfinal payload). An import configured with an async helper cannot carry\nits own transform, output filter, or preSavePage hook — put that\nprocessing in the result export instead."},"ignoreLookupName":{"type":"string","description":"Name of an entry in `lookups` used to check whether each record already exists, so `ignoreMissing`/`ignoreExisting` can skip it. Use it instead of `ignoreExtract` when existence must be resolved by a call to the target system rather than read from a field on the incoming record."},"lookups":{"description":"Lookups referenced by `existingLookupName`/`ignoreLookupName` to resolve record existence against the destination system at runtime.","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia ``lookupName`` and Handlebars expressions reference it via\n``{{lookup.name}}``. Must be unique within this\nresource's ``lookups`` array.\n"},"method":{"type":"string","enum":["GET","POST","PUT","PATCH","DELETE"],"description":"HTTP method used to issue the lookup request. ``GET`` and\n``POST`` are most common. Omit when using a static ``map`` or\n``_lookupCacheId``.\n"},"relativeURI":{"type":"string","description":"HTTP path (relative to the connection's base URL) that\nresolves the lookup. Use Handlebars with triple braces to\ninject values from the incoming record — e.g.\n``/customers?email={{{email}}}`` or\n``/accounts/{{{accountId}}}``.\n"},"postBody":{"type":"string","description":"Request body template for POST / PUT / PATCH lookup methods.\nTypically a JSON string with Handlebars placeholders —\n``{\"email\":\"{{{email}}}\"}``. Ignored for GET / DELETE.\n"},"headers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}},"description":"Optional custom headers to attach to the lookup request.\nUseful for endpoints that need a different Accept header or\nan auxiliary auth token separate from the connection-level\nheaders.\n"},"extract":{"type":"string","description":"JSONPath expression that selects the lookup value from the\nHTTP response body. ``$.`` prefix optional. Examples:\n``$.data[0].id``, ``$.results.primaryKey``, ``id``.\n"},"_lookupCacheId":{"type":"string","format":"objectId","description":"Optional reference to a LookupCache resource. When set, the\nlookup reads from the cache instead of issuing a live HTTP\nrequest — useful for stable reference data that should be\npre-loaded.\n"},"map":{"type":["object","null"],"description":"Optional static key→value object evaluated BEFORE the HTTP\nrequest. When the incoming value matches a key, the mapped\nvalue is returned without hitting the remote endpoint. Null\non dynamic lookups with no static map.\n"},"default":{"type":["string","null"],"description":"Value returned when the HTTP request returns no usable\nmatch. When omitted and ``allowFailures`` is false, an\nunmatched lookup halts the record. Stored as null when no\nfallback is configured.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, a lookup miss (no matching response and no\n``default``) resolves to ``null`` and the import continues.\nWhen false (default), a miss halts the record. May be\nstored as null (treated as unset).\n"},"useDefaultOnMultipleMatches":{"type":"boolean","description":"Controls behaviour when the response contains more than one\nmatch. When true, the ``default`` value is returned instead\nof raising an ambiguous-match error. When false (default),\nmultiple matches are treated as a failure.\n"}}},"type":"array"}}},"Ftp":{"type":"object","description":"Defines where files are written on 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 target folder and the file naming comes from\nthe import's file configuration.","required":["directoryPath"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies partner-specific B2B settings for this import.\nWhen set, the import inherits the connector's pre-configured settings; omit to use only\nthe FTP connection details."},"directoryPath":{"type":"string","description":"Directory on the server where imported files are written, either absolute or relative to\nthe login directory; the FTP user must have write permission on it. Use forward slashes\nregardless of server OS — paths are case-sensitive on UNIX/Linux servers. Supports\nhandlebars templates."},"fileName":{"type":"string","description":"Name of the file written to the server, including its extension; do not include directory\nseparators — the location comes from directoryPath. Supports handlebars placeholders such\nas items-{{timestamp}}.csv to generate a unique name per run."},"inProgressFileName":{"type":"string","description":"Temporary name the file carries while its upload is in progress, preventing other systems\nfrom processing a partially transferred file; it is renamed to its final name once the\nupload completes. Include a handlebars uniqueness token (e.g. {{timestamp}}) when one file\nis written per flow run."},"backupDirectoryPath":{"type":"string","description":"Directory on the same server where a copy of each written file is retained after a\nsuccessful import; if omitted, no server-side backup is kept. Supports static paths or\nhandlebars templates."}}},"Jdbc":{"type":"object","description":"Configuration for JDBC import operations. Defines how data is written to a database\nvia a JDBC connection.\n\n**Query type determines which fields are required**\n\n| queryType        | Required fields              | Do NOT set        |\n|------------------|------------------------------|-------------------|\n| [\"per_record\"]   | query (array of SQL strings) | bulkInsert        |\n| [\"per_page\"]     | query (array of SQL strings) | bulkInsert        |\n| [\"bulk_insert\"]  | bulkInsert object            | query             |\n| [\"bulk_load\"]    | bulkLoad object              | query             |\n\n**Critical:** query IS AN ARRAY OF STRINGS\nThe query field must be an array of plain strings, NOT a single string and NOT an array of objects.\nCorrect: [\"INSERT INTO users (name) VALUES ('{{{name}}}')\"]\nWrong: \"INSERT INTO users ...\"\nWrong: [{\"query\": \"INSERT INTO users ...\"}]","required":["queryType"],"properties":{"query":{"type":"array","items":{"type":"string"},"description":"Array of SQL query strings to execute. REQUIRED when queryType is [\"per_record\"] or [\"per_page\"].\n\nEach element is a complete SQL statement as a plain string. Typically contains a single query.\n\nUse Handlebars {{fieldName}} syntax to inject values from incoming records.\n\n**Format — array of strings**\n- CORRECT: [\"INSERT INTO users (name, email) VALUES ('{{{name}}}', '{{{email}}}')\"]\n- WRONG: \"INSERT INTO users ...\"  (not an array)\n- WRONG: [{\"query\": \"INSERT INTO users ...\"}]  (objects are invalid — causes Cast error)\n\n**Examples**\n- INSERT: [\"INSERT INTO users (name, email) VALUES ('{{{name}}}', '{{{email}}}')\"]\n- UPDATE: [\"UPDATE inventory SET qty = {{{quantity}}} WHERE sku = '{{{sku}}}'\"]\n- MERGE/UPSERT: [\"MERGE INTO target USING (SELECT CAST(? AS VARCHAR) AS email) AS src ON target.email = src.email WHEN MATCHED THEN UPDATE SET name = ? WHEN NOT MATCHED THEN INSERT (name, email) VALUES (?, ?)\"]\n\n**String vs numeric values in handlebars**\n- Use triple braces so the raw value is emitted; double braces emit the value already single-quoted, so wrapping them in quotes double-quotes it.\n- Strings: supply the quotes yourself — '{{{name}}}'\n- Numbers: no quotes — {{{quantity}}}"},"queryType":{"type":"array","items":{"type":"string","enum":["bulk_insert","per_record","per_page","bulk_load"]},"description":"Execution strategy for the SQL operation. REQUIRED. Must be an array with one value.\n\n**Decision tree**\n\n1. If UPDATE or UPSERT/MERGE → [\"per_record\"] (set query field)\n2. If INSERT with \"ignore existing\" / \"skip duplicates\" / match logic → [\"per_record\"] (set query field)\n3. If pure INSERT with no duplicate checking → [\"bulk_insert\"] (set bulkInsert object)\n4. If high-volume bulk load → [\"bulk_load\"] (set bulkLoad object)\n\n**Critical relationship to other fields**\n| queryType        | REQUIRES              | DO NOT SET   |\n|------------------|-----------------------|--------------|\n| [\"per_record\"]   | query (array)         | bulkInsert   |\n| [\"per_page\"]     | query (array)         | bulkInsert   |\n| [\"bulk_insert\"]  | bulkInsert object     | query        |\n| [\"bulk_load\"]    | bulkLoad object       | query        |\n\n**Examples**\n- Per-record upsert: [\"per_record\"]\n- Bulk insert: [\"bulk_insert\"]\n- Bulk load: [\"bulk_load\"]\n"},"bulkInsert":{"type":"object","description":"Bulk insert configuration. REQUIRED when queryType is [\"bulk_insert\"]. DO NOT SET when queryType is [\"per_record\"].\n\nEnables efficient batch insertion of records into a database table without per-record SQL.\n","properties":{"tableName":{"type":"string","description":"Target database table name for bulk insert. REQUIRED.\nCan include schema qualifiers (e.g., \"schema.tableName\").\n"},"batchSize":{"type":"string","description":"Number of records per batch during bulk insert.\nLarger values improve throughput but use more memory.\nCommon values: \"1000\", \"5000\".\n"}}},"bulkLoad":{"type":"object","description":"Bulk load configuration. REQUIRED when queryType is [\"bulk_load\"]. Uses database-native bulk loading for maximum throughput.\n","properties":{"tableName":{"type":"string","description":"Target database table name for bulk load. REQUIRED.\nCan include schema qualifiers (e.g., \"schema.tableName\").\n"},"primaryKeys":{"type":["array","null"],"items":{"type":"string"},"description":"Primary key column names for upsert/merge during bulk load.\nWhen set, existing rows matching these keys are updated; non-matching rows are inserted.\nExample: [\"id\"] or [\"order_id\", \"product_id\"] for composite keys.\n"},"overrideMergeOrInsertQuery":{"type":"boolean","description":"When true, a custom merge/insert query replaces the auto-generated statement for the\nbulk load, enabling ignore-existing logic or conditional updates."}}},"lookups":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia `lookupName` and Handlebars expressions reference it via\n`{{lookup \"name\" value}}`. Must be unique within this\nresource's `lookups` array."},"query":{"type":"string","description":"SQL query for the lookup (e.g., \"SELECT id FROM users WHERE email = '{{{email}}}'\")."},"extract":{"type":"string","description":"JSONPath-style expression that selects the value from the\nquery result row (e.g. `id`, `details.price`). Omit when\nthe query returns a single scalar."},"_lookupCacheId":{"type":"string","format":"objectId","description":"Optional reference to a LookupCache resource. When set, the\nlookup reads from the cache instead of issuing a live SQL\nquery — useful for stable reference data that should be\npre-loaded."},"allowFailures":{"type":"boolean","description":"When true, a lookup miss (no matching row and no `default`)\nresolves to `null` and the import continues. When false\n(default), a miss halts the record."},"map":{"type":"object","description":"Optional static key→value object evaluated BEFORE the SQL\nquery. When the incoming value matches a key, the mapped\nvalue is returned without querying the database."},"default":{"type":"string","description":"Value returned when the query matches no rows. When omitted\nand `allowFailures` is false, an unmatched lookup halts the\nrecord."}}},"description":"Lookup definitions executed against the JDBC connection. Each entry\nruns a `SELECT` and extracts a value used by field mappings\n(referenced via `lookupName`) or Handlebars expressions\n(referenced via `{{lookup \"name\" value}}`)."}}},"Mongodb":{"type":"object","description":"Configuration for MongoDB imports. Contains only the MongoDB-specific properties; import-level fields such as ignoreExisting, ignoreMissing, name, and description are set on the parent import, not here.","required":["method","collection"],"properties":{"method":{"type":"string","enum":["insertMany","updateOne"],"description":"Write operation performed against the MongoDB collection for each record."},"collection":{"type":"string","description":"MongoDB collection that receives the imported records.\nCollection names are case-sensitive and must not start with the reserved \"system.\" prefix."},"filter":{"type":"string","description":"MongoDB query filter that selects the document each updateOne applies to; required when\nmethod is updateOne. Supports standard MongoDB query operators and dot notation, and\nhandlebars placeholders to reference the incoming record's fields."},"document":{"type":"string","description":"Content of the document written to the collection (used when method is insertMany), as a JSON string that can include nested documents and arrays and handlebars placeholders."},"update":{"type":"string","description":"Modifications applied to matching documents (used when method is updateOne), using MongoDB update operators such as $set, $inc, or $push.\nA complete document without operators replaces the matched document entirely."},"upsert":{"type":"boolean","description":"When true, inserts a new document built from the update criteria if no existing document matches the filter.\nWhen false, records with no matching document are skipped and nothing is inserted."},"ignoreExtract":{"type":"string","description":"Path to the field in the source record that identifies existing records. When that field has a value, the record is treated as existing and skipped.\nOnly used when the import-level ignoreExisting flag is true; do not set it otherwise.\nEnclose field names containing special characters in square brackets (for example, [vendor-code]), and reference array items with an index, such as items[0].id."},"ignoreLookupFilter":{"type":"string","description":"JSON-stringified MongoDB query filter used to find existing documents in the target collection when the import-level ignoreExisting flag is true.\nIf the query matches a document, the incoming record is treated as existing and skipped.\nUse Handlebars placeholders to reference values from the incoming record, for example \"{\\\"email\\\":\\\"{{email}}\\\"}\".\nUnlike ignoreExtract, which only checks whether a field on the incoming record has a value, this filter queries the MongoDB collection itself."}},"if":{"properties":{"method":{"const":"updateOne"}},"required":["method"]},"then":{"required":["filter"]}},"NetSuite":{"type":"object","description":"Configuration for NetSuite imports (legacy adaptor; use netsuite_da / NetSuiteDistributedImport for SuiteApp 2.0).","properties":{"lookups":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique name for this lookup, referenced from mappings and handlebars templates with `{{lookup 'name' value}}`."},"recordType":{"type":"string","description":"NetSuite record type searched by a dynamic lookup (for example, `customer`, `salesOrder`). Set with `searchField` or `expression`; omit for a static `map` lookup."},"searchField":{"type":"string","description":"NetSuite field the dynamic lookup searches on (for example, `externalid`). Must be a searchable field on `recordType`. Use this or `expression`."},"expression":{"type":"string","description":"NetSuite search expression evaluated at runtime to select matching records (for example, `[\"recipient\",\"is\",\"123\"]`). Use this or `searchField`."},"resultField":{"type":"string","description":"Field on the matched NetSuite record whose value the lookup returns (for example, `internalid`).\nSupports dot notation for nested fields."},"map":{"type":["object","null"],"description":"Static lookup table with input values as keys and their corresponding output values. Use instead of a NetSuite search for fixed value translations. The platform stores `null` here on search-driven lookups."},"default":{"type":["string","null"],"description":"Fallback value returned when the lookup finds no match. Used together with `allowFailures`. Stored as `null` when not configured."},"allowFailures":{"type":"boolean","description":"When true, an unmatched lookup falls back to `default` and processing continues; when false, an unmatched lookup fails the record."},"_id":{"type":"object","description":"Server-assigned unique identifier for this lookup entry.","readOnly":true}}},"description":"Lookup definitions used to resolve reference values during the import, using either a static `map` or a dynamic NetSuite record search."},"operation":{"type":"string","enum":["add","update","addupdate","attach","detach","delete"],"description":"Operation to perform on NetSuite records, as a plain string (the API lowercases the value).\nFor `update`, `addupdate`, and `delete`, also set internalIdLookup so existing records can be found; for `add` with ignoreExisting, set internalIdLookup to check for duplicates before creating.\nDefault to `addupdate` when the intent is \"sync\", \"upsert\", or \"create or update\"; use `add` for plain \"create\" or \"insert\".\nDo not wrap the value in an object like `{\"type\": \"addupdate\"}` — that fails validation."},"isFileProvider":{"type":"boolean","description":"When true, enables file operations against NetSuite — browsing, uploading, downloading, updating, and deleting files.\nWhen false or omitted, file-related operations are unavailable through this import."},"customFieldMetadata":{"type":"object","description":"Metadata describing the custom fields defined on the target NetSuite record type, used to validate and process custom field data during the import."},"recordType":{"type":"string","description":"NetSuite record type this import writes to (e.g. customer, salesOrder, invoice).\nUse the exact NetSuite internal record type identifier; both standard and custom record types are supported."},"recordTypeId":{"type":"string","description":"The unique identifier specifying the record type within NetSuite. Determines the applicable schema, fields, validation rules, and processing logic. Use exact NetSuite record type identifiers such as standard types or custom record IDs."},"retryUpdateAsAdd":{"type":"boolean","description":"When true, an update that fails because the target record does not exist is automatically retried as an add.\nWhen false or unset, update failures error immediately without retrying.\nMay create duplicate records if the update failed for a reason other than a missing record."},"batchSize":{"type":"number","description":"Number of records sent to NetSuite per API call.\nLarger batches reduce the number of calls but increase memory use and timeout risk per call."},"internalIdLookup":{"type":"object","properties":{"extract":{"type":"string","description":"Path used to extract the value for the internal ID lookup."},"searchField":{"type":"string","description":"The NetSuite record field used as the key attribute for the internal ID lookup search. Should be a unique or indexed field to ensure accurate matches. Must correspond to a valid, searchable field on the target record type."},"expression":{"type":"string","description":"NetSuite search expression that filters which records match the lookup.\nSupports logical operators (AND, OR, NOT), comparison operators, and nested conditions, evaluated at runtime against current data."}},"description":"Configuration for locating existing NetSuite records by internal ID.\nRequired when operation is `update`, `addupdate`, or `delete`; also used with `add` plus ignoreExisting to check for duplicates."},"preferences":{"type":"object","properties":{"ignoreReadOnlyFields":{"type":"boolean","description":"When true, silently skips read-only fields during update operations instead of raising errors."},"warningAsError":{"type":"boolean","description":"When true, treats NetSuite warnings as errors, causing the operation to fail immediately."},"skipCustomMetadataRequests":{"type":"boolean","description":"When true, skips custom metadata requests during NetSuite API operations to improve performance."}},"description":"Preferences that control how NetSuite handles the import operation."},"file":{"type":"object","properties":{"name":{"type":"string","description":"Filename for the file in the NetSuite File Cabinet, without any folder or path information.\nMust be unique within its folder; include the file extension (e.g. .pdf, .csv)."},"fileType":{"type":"string","description":"NetSuite file type that governs how the file is processed, stored, and displayed.\nMust match the actual content format of the file."},"folder":{"type":"string","description":"Folder in the NetSuite File Cabinet where the file is stored, as a numeric folder ID or a folder path.\nUpdating this on an existing file moves the file to the specified folder."},"folderInternalId":{"type":"string","description":"Internal ID of the File Cabinet folder where the file is stored.\nMust reference an existing folder; changing it moves the file to a different folder."},"internalId":{"type":"string","description":"NetSuite internal ID of an existing file, used to target it for retrieval, updates, or deletion.\nAssigned by NetSuite when the file is created and cannot be changed."},"backupFolderInternalId":{"type":"string","description":"Internal ID of the File Cabinet folder where backup files are stored."}},"description":"Configuration for the file to upload to or update in the NetSuite File Cabinet."}},"if":{"not":{"propertyNames":{"enum":["preferences","lookups"]}}},"then":{"required":["operation"],"if":{"properties":{"isFileProvider":{"const":true}},"required":["isFileProvider"]},"else":{"required":["recordType"]}}},"NetsuiteDistributed":{"type":"object","description":"Configuration for NetSuite Distributed (SuiteApp 2.0) import operations — the primary sub-schema for the NetSuiteDistributedImport adaptorType. The API field name is \"netsuite_da\".\noperation and recordType are required. The server may attach empty bookkeeping stubs of\nthis object (empty lookups/mapping, or missingOrCorruptedDAConfig) to imports of other\nadaptor types; stubs omit these fields. Set internalIdLookup so existing records\ncan be found when operation is update, addupdate, or delete.","properties":{"operation":{"type":"string","enum":["add","update","addupdate","attach","detach","delete"],"description":"Operation to perform on the target NetSuite record, as a plain string. Required.\nFor `update`, `addupdate`, and `delete`, also set internalIdLookup so existing records can be found."},"recordType":{"type":"string","description":"NetSuite record type to import into (e.g. \"customer\", \"salesorder\", \"customrecord_myrecord\" for custom record types). Required.\nMust match a valid NetSuite record type identifier."},"recordIdentifier":{"type":"string","description":"Custom record identifier, used to identify the specific record type when\nimporting into custom record types.\n"},"restletVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"Version of the NetSuite RESTlet to use, as a plain string.\nDefaults to \"suiteapp2.0\" when useSS2Restlets is true, \"suitebundle\" otherwise; rarely needs to be set explicitly for modern integrations.\nThe version is fixed when the step is created — the Advanced selector is disabled on existing steps, and migrating an existing step to a different version means recreating or cloning it."},"useSS2Restlets":{"type":"boolean","description":"When true, uses SuiteScript 2.0 RESTlets and restletVersion defaults to \"suiteapp2.0\".\nDefaults to true for modern integrations.\nSet at step creation together with restletVersion — the same creation-time-only constraint applies (see restletVersion)."},"missingOrCorruptedDAConfig":{"type":"boolean","description":"When true, the Distributed Adaptor configuration is missing or corrupted.\nSet by the system — do not set manually."},"batchSize":{"type":"number","description":"Number of records to process per batch. Controls how many records are sent\nto NetSuite in a single API call. Typical values: 50-200.\n"},"internalIdLookup":{"type":"object","description":"Configuration for looking up existing NetSuite records to match against incoming data.\nRequired when operation is \"update\", \"addupdate\", or \"delete\".","properties":{"extract":{"type":"string","description":"Path in the source record to extract the lookup value from (e.g. \"internalId\" or \"externalId\")."},"searchField":{"type":"string","description":"NetSuite field to search against (e.g. \"externalId\", \"email\", \"name\", \"tranId\")."},"operator":{"type":"string","description":"Comparison operator for the lookup (e.g. \"is\", \"contains\", \"startswith\")."},"expression":{"type":"string","description":"NetSuite search expression for complex lookup conditions, used for multi-field or conditional lookups."}}},"hooks":{"type":"object","description":"Script hooks for custom processing at different stages of the import.\nEach hook references a SuiteScript file and function.\n","properties":{"preMap":{"type":"object","description":"Runs before field mapping is applied.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}},"postMap":{"type":"object","description":"Runs after field mapping, before submission to NetSuite.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}},"postSubmit":{"type":"object","description":"Runs after the record is submitted to NetSuite.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}}}},"mapping":{"type":"object","description":"NetSuite-specific Mapper 1.0 field mappings (the \"DA\" dialect).\nUsed by `NetSuiteDistributedImport` exclusively — NetSuite's\nDistributed Adaptor cannot consume generic v1 mappings or the\nMapper 2.0 shape because it needs sublist, subrecord, and\nconditional lookup semantics the generic schemas don't model.\n\nField-level shape: `fields` for body columns, `lists` for\nsublists / line-item arrays.","properties":{"fields":{"type":"array","items":{"type":"object","properties":{"extract":{"type":["string","null"],"description":"Path in the source record to extract the value from.\nUse dot notation for nested fields (e.g., \"address.city\").\nThe platform stores `null` here on fields driven by\n`hardCodedValue` instead of an extract path.\n"},"generate":{"type":"string","description":"The NetSuite field ID to write the value to (e.g., \"companyname\", \"email\", \"subsidiary\").\n"},"hardCodedValue":{"type":"string","description":"A static value to always use instead of extracting from source data.\nMutually exclusive with extract.\n"},"lookupName":{"type":"string","description":"Reference to a lookup defined in netsuite_da.lookups by name."},"dataType":{"type":"string","description":"Data type hint for the field value (e.g., \"string\", \"number\", \"date\", \"boolean\").\n"},"internalId":{"type":"boolean","description":"When true, the value is a NetSuite internal ID reference."},"immutable":{"type":"boolean","description":"When true, this field is only set on record creation, not on updates."},"discardIfEmpty":{"type":"boolean","description":"When true, skip this field mapping if the extracted value is empty."},"extractDateFormat":{"type":"string","description":"Date format of the extracted value (e.g., \"MM/DD/YYYY\", \"ISO8601\").\nUsed to parse date strings from source data.\n"},"extractDateTimezone":{"type":["string","null"],"description":"Timezone of the extracted date value (e.g., \"America/New_York\"). Stored as `null` when not configured."},"subRecordMapping":{"type":"object","description":"Nested Mapper 1.0 mapping for a NetSuite subrecord\nembedded in this field.  A subrecord is a child\nrecord that only exists inside a parent (e.g. the\nInventory Detail on a serialized-item line of a\nsales order); NetSuite reaches it through the\nparent.\n\n**Recursive structure.**  The nested `mapping`\nobject has the same `{fields, lists}` shape as the\ntop-level `netsuite_da.mapping`.  Recursion is\nallowed but rare.","properties":{"recordType":{"type":"string","description":"NetSuite's internal record-type id of the\nSUBRECORD (not the parent).  E.g.\n`inventorydetail`, `itempricing`,\n`mainaddress`.\n"},"jsonPath":{"type":"string","description":"JSONPath into the source record that selects the\ndata feeding this subrecord.  Semantics depend\non placement:\n* Line-level (on `lists[].fields[].`\n  `subRecordMapping`) — often `$` to reuse\n  the enclosing sublist line's row, or a path\n  into a nested array on that row.\n* Body-level (on `fields[].subRecordMapping`)\n  — a path into the source record root.\nDefaults to `$`.\n"},"mapping":{"type":"object","description":"Nested `{fields, lists}` mapping for the\nsubrecord.  Identical shape to the top-level\n`netsuite_da.mapping`.  Subrecords often have\nempty `fields` and only a sublist (e.g.\n`inventorydetail` → `inventoryassignment`).\n"},"lookups":{"type":"array","description":"Subrecord-scoped lookups.  Same shape as the\ntop-level `netsuite_da.lookups`.  Fields inside\nthis `subRecordMapping.mapping` reference them\nvia `lookupName`.  Often `[]`.\n"},"referenceFieldId":{"type":"string","description":"Secondary reference-field identifier used on a\nsmall fraction of subrecord blocks in production\n(~3%).  Exact semantics are not publicly\ndocumented by Celigo — match the value on\nexisting resources when present; do NOT\nsynthesize one.  Leave unset unless a template\nclearly requires it.\n"}}},"conditional":{"type":"object","description":"Conditional logic for when to apply this field mapping\n(\"Only perform mapping when\" in the UI). Lookup-based\nconditions evaluate the lookup named by `lookupName`,\nwhich must be defined on this import.\n","properties":{"lookupName":{"type":"string","description":"Lookup to evaluate for the condition. Must exactly match the\n`name` of a lookup defined on this import."},"when":{"type":"string","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","ignore_if_set"],"description":"Condition that controls when this field mapping is applied."}}}}},"description":"Body-level field mappings. Each entry maps a source field to a NetSuite body field.\n"},"lists":{"type":"array","items":{"type":"object","properties":{"generate":{"type":"string","description":"The NetSuite sublist ID (e.g., \"item\" for sales order line items,\n\"addressbook\" for address sublists).\n"},"jsonPath":{"type":"string","description":"LEGACY — do not set. Nominally the JSON path to the array of sublist records, but NetSuite DA ignores a top-level jsonPath here."},"fields":{"type":"array","items":{"type":"object","properties":{"extract":{"type":["string","null"],"description":"Path from the source record root that produces\nthe value for this sublist column. Paths are\nalways anchored at the source record root —\nnever relative to a pre-iterated row. The\nplatform stores `null` here on fields driven by\n`hardCodedValue` instead of an extract path."},"generate":{"type":"string","description":"NetSuite sublist field ID to write to."},"hardCodedValue":{"type":"string","description":"Static value for this sublist field."},"lookupName":{"type":"string","description":"Reference to a lookup by name."},"dataType":{"type":"string","description":"Data type hint for the field value."},"internalId":{"type":"boolean","description":"When true, the value is a NetSuite internal ID reference."},"isKey":{"type":"boolean","description":"When true, this field is a key field for matching existing sublist lines."},"immutable":{"type":"boolean","description":"When true, this field is only set on record creation, not on updates."},"discardIfEmpty":{"type":"boolean","description":"When true, skips this field mapping if the extracted value is empty."},"extractDateFormat":{"type":"string","description":"Date format of the extracted value."},"extractDateTimezone":{"type":["string","null"],"description":"Timezone of the extracted date value. Stored as `null` when not configured."},"subRecordMapping":{"type":"object","description":"Nested Mapper 1.0 mapping for a NetSuite\nsubrecord embedded in this sublist-field.  A\nsubrecord is a child record that only exists\ninside a parent (e.g. the Inventory Detail on\na serialized-item line of a sales order);\nNetSuite reaches it through the parent.\n\n**Recursive structure.**  The nested\n`mapping` object has the same\n`{fields, lists}` shape as the top-level\n`netsuite_da.mapping`.  Recursion is allowed\nbut rare.","properties":{"recordType":{"type":"string","description":"NetSuite's internal record-type id of the\nSUBRECORD (not the parent).  E.g.\n`inventorydetail`, `itempricing`,\n`componentinventorydetail`.\n"},"jsonPath":{"type":"string","description":"JSONPath (relative to the current sublist\nline) to the rows that populate the\nsubrecord.  Typically `$` when the\nsubrecord data is carried on the same\nline item (the enclosing\n`lists[].jsonPath` has already selected\nthe line).  Defaults to `$`.\n"},"mapping":{"type":"object","description":"Nested `{fields, lists}` mapping for the\nsubrecord.  Identical shape to the\ntop-level `netsuite_da.mapping`.\nSubrecords often have empty `fields` and\nonly a sublist (e.g. `inventorydetail` →\n`inventoryassignment`).\n"},"lookups":{"type":"array","description":"Subrecord-scoped lookups.  Same shape as\nthe top-level `netsuite_da.lookups`.\nFields inside this\n`subRecordMapping.mapping` reference\nthem via `lookupName`.  Often `[]`.\n"},"referenceFieldId":{"type":"string","description":"Secondary reference-field identifier used\non a small fraction of subrecord blocks\nin production (~3%).  Exact semantics are\nnot publicly documented by Celigo — match\nthe value on existing resources when\npresent; do NOT synthesize one.  Leave\nunset unless a template clearly requires\nit.\n"}}},"conditional":{"type":"object","description":"Conditional logic for when to apply this sublist field mapping\n(\"Only perform mapping when\" in the UI). Lookup-based conditions\nevaluate the lookup named by `lookupName`, which must be defined\non this import.","properties":{"lookupName":{"type":"string","description":"Lookup to evaluate for the condition. Must exactly match the\n`name` of a lookup defined on this import."},"when":{"type":"string","description":"Condition that controls when this field mapping is applied.","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","ignore_if_set"]}}}}},"description":"Field mappings for each column in the sublist."}}},"description":"Sublist (line-item) mappings. Each entry maps source data to a NetSuite sublist.\n"}}},"lookups":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia `lookupName` and Handlebars expressions reference it via\n`{{lookup \"name\" value}}`. Must be unique within this\nresource's `lookups` array."},"recordType":{"type":"string","description":"NetSuite record type to search. Use the internal record-type\nid (lowercase, underscored), e.g. `customer`, `item`,\n`salesorder`, `customlist_xxx`."},"searchField":{"type":"string","description":"NetSuite field on `recordType` to match against. Use the\ninternal field id — e.g. `email`, `externalid`,\n`internalid`, `entityid`, `custitem_xxx`."},"resultField":{"type":"string","description":"NetSuite field whose value is returned when a record matches.\nTypically `internalid` when the lookup resolves to a record\nreference, or any queryable scalar field."},"expression":{"type":"string","description":"Optional advanced-search expression (NetSuite saved-search\nDSL) for multi-criterion lookups. When present, supersedes\n`searchField` / `operator` — those fields are ignored."},"operator":{"type":"string","description":"Comparison operator for the `searchField` match. Common\nvalues: `is`, `contains`, `startswith`, `anyof`,\n`equalto`, `greaterthan`."},"includeInactive":{"type":"boolean","description":"When true, includes inactive records in lookup results."},"useDefaultOnMultipleMatches":{"type":"boolean","description":"Controls behaviour when the search matches more than one\nrecord. When true, the `default` value is returned instead\nof raising an ambiguous-match error. When false (default),\nmultiple matches are treated as a failure."},"allowFailures":{"type":["boolean","null"],"description":"When true, a lookup miss (no matching record and no\n`default`) resolves to `null` and the import continues.\nWhen false (default), a miss halts the record. May be\nstored as null (treated as unset)."},"map":{"type":["object","null"],"description":"Optional static key→value object evaluated BEFORE the\nNetSuite search. When the incoming value matches a key, the\nmapped value is returned without hitting NetSuite. Null on\ndynamic (search-based) lookups with no static map."},"default":{"type":["string","null"],"description":"Value returned when the search matches no records. When\nomitted and `allowFailures` is false, an unmatched lookup\nhalts the record. May be stored as null (no default)."}}},"description":"Lookup definitions that resolve reference values from NetSuite.\nReferenced by name from field mappings via `lookupName` and from\nHandlebars expressions via `{{lookup \"name\" value}}`."},"rawOverride":{"type":"object","description":"Raw override object for advanced use cases. When useRawOverride is true,\nthis object is sent directly to the NetSuite API, bypassing normal mapping.\n"},"useRawOverride":{"type":"boolean","description":"When true, uses rawOverride instead of the normal mapping configuration.\n"},"isMigrated":{"type":"boolean","description":"When true, this import was migrated from a legacy format. Set by the system."},"retryUpdateAsAdd":{"type":"boolean","description":"When true, if an update fails because the record doesn't exist, automatically\nretry as an add operation. Useful for initial syncs where records may not exist yet.\n"},"customFieldMetadata":{"type":"object","description":"Metadata about custom fields on the target record type.\nPopulated by the system from NetSuite metadata — do not set manually.\n"},"file":{"type":"object","description":"File cabinet configuration for file-based imports into NetSuite.\n","properties":{"name":{"type":"string","description":"Filename for the file in NetSuite File Cabinet."},"fileType":{"type":"string","description":"NetSuite file type (e.g., \"PDF\", \"CSV\", \"PLAINTEXT\", \"EXCEL\", \"XML\").\n"},"folder":{"type":"string","description":"Folder path or name in the NetSuite File Cabinet."},"folderInternalId":{"type":"string","description":"Internal ID of the target folder in NetSuite File Cabinet."},"internalId":{"type":"string","description":"Internal ID of an existing file to update."},"backupFolderInternalId":{"type":"string","description":"Internal ID of a backup folder for file versioning."}}},"isFileProvider":{"type":"boolean","description":"Whether this import handles files in the NetSuite File Cabinet.\n"},"preferences":{"type":"object","properties":{"ignoreReadOnlyFields":{"type":"boolean","description":"When true, silently skip read-only fields instead of raising errors."},"warningAsError":{"type":"boolean","description":"When true, treat NetSuite warnings as errors that stop the import."},"skipCustomMetadataRequests":{"type":"boolean","description":"When true, skip fetching custom field metadata to improve performance."}},"description":"Import behavior preferences that control how NetSuite handles the import operation.\n"},"recordTypeId":{"type":"string","description":"The internal record type ID. Used for custom record types in NetSuite where\nthe numeric ID is needed in addition to the recordType string.\n"}},"if":{"not":{"propertyNames":{"enum":["lookups","mapping","missingOrCorruptedDAConfig"]}}},"then":{"required":["operation","recordType"],"if":{"properties":{"operation":{"enum":["update","addupdate","delete"]}},"required":["operation"]},"then":{"required":["internalIdLookup"]}}},"Rdbms":{"type":"object","description":"Configuration for RDBMS imports into SQL Server, MySQL, PostgreSQL, Snowflake, Oracle, MariaDB, and other relational databases.\nqueryType determines which companion field is required: per_record, per_page, and first_page use the query field; bulk_insert uses the bulkInsert object; bulk_load uses the bulkLoad object — never set query and bulkInsert together.\n(The server itself stores an empty `query: []` next to bulk_insert/bulk_load configs and may leave an empty `bulkInsert: {}` stub next to others — empty companions are normal in responses; only a populated conflicting companion is invalid.)\nThe query field is an array of plain SQL strings, not a single string and not an array of objects.","properties":{"lookups":{"$ref":"#/components/schemas/Lookups","description":"Lookup definitions executed against the RDBMS connection. Each\nentry runs a `SELECT` and extracts a value used by field mappings\n(referenced via `lookupName`) or Handlebars expressions\n(referenced via `{{lookup \"name\" value}}`).\n\n**Mirror invariant**\nThe Celigo platform persists lookups in TWO locations on an RDBMS\nimport: top-level `lookups` and `rdbms.lookups`. Both arrays\nmust hold byte-for-byte identical content — the MappingsAgent\nwrites both on every save.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia `lookupName` and Handlebars expressions reference it via\n`{{lookup \"name\" value}}`. Must be unique within this\nresource's `lookups` array.\n"},"query":{"type":"string","description":"SQL `SELECT` statement that resolves the lookup value.\nTypically returns a single row with a single column; use\n`extract` to pick the column when the query returns multiple.\nInject values from the incoming record with Handlebars — RDBMS\nqueries use triple braces and the `record.` prefix:\n`SELECT id FROM users WHERE email = '{{{record.email}}}'`.\n"},"extract":{"type":"string","description":"JSONPath-style expression that selects the value from the query\nresult row (e.g. `id`, `details.price`). Omit when the\nquery returns a single scalar — the first column is used\nautomatically.\n"},"_lookupCacheId":{"type":"string","format":"objectId","description":"Optional reference to a LookupCache resource. When set, the\nlookup reads from the cache instead of issuing a live SQL\nquery — useful for stable reference data that should be\npre-loaded.\n"},"map":{"type":"object","description":"Optional static key→value object evaluated BEFORE the SQL query.\nWhen the incoming value matches a key, the mapped value is\nreturned without querying the database. Useful for small\ncontrolled enumerations.\n"},"default":{"type":"string","description":"Value returned when the query matches no rows. When omitted and\n`allowFailures` is false, an unmatched lookup halts the\nrecord.\n"},"allowFailures":{"type":"boolean","description":"When true, a lookup miss (no matching row and no `default`)\nresolves to `null` and the import continues. When false\n(default), a miss halts the record.\n"}}},"type":"array"},"query":{"type":"array","items":{"type":"string"},"description":"SQL statements to execute; required when queryType is `[\"per_record\"]`, `[\"per_page\"]`, or `[\"first_page\"]`, and typically contains a single statement.\nEach element is a complete SQL statement as a plain string — never a single bare string and never an array of objects (objects cause a Cast error).\nInject values from the incoming record with Handlebars using the `record.` prefix and triple braces, e.g. `'{{{record.name}}}'` for strings (single-quoted in the SQL) and `{{{record.quantity}}}` for numbers.\nRendering is strict: a reference the render model cannot resolve (a bare `{{name}}` without prefix, or `record.*` when the arriving record is a grouped array) fails the step with `cannot_evaluate_handlebars` — it does not render an empty value.\n`record` and `rows` are aliases chosen by the arriving data's shape: an object record binds `record` (`rows` undefined); a grouped array-record (e.g. upstream `groupByFields`) binds `rows` (`record` undefined) — address grouped rows as `{{rows.0.field}}` or iterate `{{#each rows}}`.\nDouble braces (`{{record.field}}`) output the value wrapped in single quotes with embedded quotes doubled, while triple braces output it raw; block helpers such as `{{#each}}` and `{{#if}}` use double braces as normal."},"queryType":{"type":["array","null"],"items":{"type":"string","enum":["INSERT","UPDATE","bulk_insert","per_record","per_page","first_page","bulk_load"]},"description":"Execution strategy for the SQL operation, which determines the required companion field: per_record, per_page, and first_page require `query`; bulk_insert requires `bulkInsert`; bulk_load requires `bulkLoad`.\nPrefer the highest-performance type the operation supports: bulk_load (currently Snowflake and NSAW — handles INSERT, upsert via `bulkLoad.primaryKeys`, and custom merge or ignore-existing logic via `bulkLoad.overrideMergeQuery`), then bulk_insert for pure INSERTs, then per_page, then per_record only when the logic cannot be expressed as a batch operation.\nFor UPDATE, UPSERT, or ignore-existing logic on databases without bulk_load support, use per_page or per_record with `query` — bulk_insert has no duplicate-checking logic.\nSet a single value; do not combine types or use the legacy INSERT and UPDATE values.\nper_page statements render against the whole batch (`batch_of_records`) rather than a single record — loop with `{{#each batch_of_records}}...{{{record.fieldName}}}...{{/each}}`.\n\n`bulk_load` — Stages data as a file and loads via database-native COPY/bulk mechanism for maximum throughput; supports INSERT, upsert via bulkLoad.primaryKeys, and custom merge or ignore-existing logic via bulkLoad.overrideMergeQuery; currently supported for Snowflake and NSAW.\n`bulk_insert` — Batch INSERT via multi-row VALUES clause for efficient bulk data loading; has no upsert or ignore-existing logic and is available for all RDBMS types.\n`per_page` — Executes one SQL statement per page (batch) of records; available for all RDBMS types.\n`per_record` — Executes one SQL statement per record, giving full control over individual record SQL; available for all RDBMS types."},"bulkInsert":{"type":"object","description":"Configuration for batch INSERTs via a multi-row VALUES clause; required when queryType is `[\"bulk_insert\"]` and must not be set otherwise.\nOnly suited to pure INSERTs — for UPDATE, UPSERT, or ignore-existing/skip-duplicates logic, use queryType `[\"per_record\"]` with the `query` field instead.","properties":{"tableName":{"type":"string","description":"The name of the database table into which the bulk insert operation will be executed. This value must correspond to a valid, existing table within the target relational database management system (RDBMS). It serves as the primary destination for inserting multiple rows of data efficiently in a single operation. The table name can include schema or namespace qualifiers if supported by the database (e.g., \"schemaName.tableName\"), allowing precise targeting within complex database structures. Proper validation and sanitization of this value are essential to ensure the operation's success and to prevent SQL injection or other security vulnerabilities."},"batchSize":{"type":"string","description":"The number of records to be inserted into the database in a single batch during a bulk insert operation. This parameter is crucial for optimizing the performance and efficiency of bulk data loading by controlling how many records are grouped together before being sent to the database. Proper tuning of batchSize balances memory consumption, transaction overhead, and throughput, enabling the system to handle large volumes of data efficiently without overwhelming resources or causing timeouts. Adjusting batchSize directly impacts transaction size, network utilization, error handling granularity, and recovery strategies, making it essential to tailor this value based on the specific database capabilities, system resources, and workload characteristics."}}},"bulkLoad":{"type":"object","properties":{"tableName":{"type":"string","description":"Target table for the bulk load.\nMust reference an existing table accessible with the connection's credentials; schema-qualified names are supported."},"primaryKeys":{"type":"array","items":{"type":"string"},"description":"Columns that uniquely identify each record in the target table, used to match existing rows during the load.\nSet for upsert behavior (a MERGE is auto-generated); omit (or empty) for a pure INSERT.\nComposite keys are supported — list every key column, and names must exactly match the target schema."},"overrideMergeQuery":{"type":"boolean","description":"When true, a custom merge query replaces the auto-generated MERGE statement, enabling ignore-existing logic, conditional updates, or multi-table operations.\nThe override SQL references `{{import.rdbms.bulkLoad.preMergeTemporaryTable}}` for the staging table."}},"description":"Configuration for bulk loading: data is staged as a file and loaded via the database's native COPY/bulk mechanism for maximum throughput.\nRequired when queryType is `[\"bulk_load\"]`; currently supported for Snowflake and NSAW."},"updateLookupName":{"type":["string","null"],"description":"Name of the lookup used for update operations in the legacy composite (queryType1) flow."},"updateExtract":{"type":["string","null"],"description":"Path used to extract the value that drives update operations in the legacy composite flow."},"ignoreLookupName":{"type":["string","null"],"description":"Name of the lookup used to determine which records to ignore when ignore-existing is enabled."},"ignoreExtract":{"type":["string","null"],"description":"Path used to extract the value that determines whether a record is ignored when ignore-existing is enabled."}},"if":{"not":{"propertyNames":{"enum":["lookups"]}}},"then":{"required":["queryType"],"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"enum":["per_record","per_page"]}}}},"then":{"required":["query"],"properties":{"bulkInsert":{"maxProperties":0}}},"else":{"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"const":"first_page"}}}},"then":{"required":["query"]},"else":{"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"const":"bulk_insert"}}}},"then":{"required":["bulkInsert"],"properties":{"query":{"maxItems":0}}},"else":{"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"const":"bulk_load"}}}},"then":{"required":["bulkLoad"],"properties":{"query":{"maxItems":0}}}}}}}},"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 set to true\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\nIf allowFailures is false and no default is specified, the flow will fail\nwhen encountering unmapped values.\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\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\nThe best practice is typically to set allowFailures to true with a meaningful\ndefault value, so flows remain operational while alerting you to missing mappings.\n"}}}},"S3":{"type":"object","description":"Defines where files are written to 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 destination, fileKey sets the object key, and backupBucket\nretains a copy after a successful import.","required":["region","bucket"],"properties":{"region":{"type":"string","default":"us-east-1","description":"AWS region where the S3 bucket resides. Must match the bucket's actual location to avoid\nconnectivity errors and misrouted requests."},"bucket":{"type":"string","description":"S3 bucket that receives the imported files. The bucket must already exist and the\nconnection's AWS credentials must have s3:PutObject permission on it."},"fileKey":{"type":"string","description":"Object key under which each file is stored in the bucket. Include slashes to organize\nfiles in a folder-style hierarchy (for example, imports/2023/12/orders.json); keys are\ncase-sensitive. Supports handlebars placeholders such as orders-{{timestamp}}.json to\ngenerate a unique key per run."},"backupBucket":{"type":"string","description":"S3 bucket where a copy of each imported file is retained after a successful import; if\nomitted, no backup copy is kept. Must reference an existing bucket the connection's\ncredentials have s3:PutObject permission on."},"serverSideEncryptionType":{"type":"string","description":"Server-side encryption applied to uploaded objects. The connection form sets `AES256`\n(SSE-S3, S3-managed keys) when encryption is enabled; leave unset to use the bucket's\ndefault encryption."}}},"Wrapper":{"type":"object","description":"Configuration for Wrapper imports, which delegate writing records 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.","properties":{"function":{"type":"string","description":"Name of the function the wrapper invokes to process records.\nMust match a callable function in the wrapper's execution context; names are case-sensitive."},"configuration":{"type":"object","additionalProperties":true,"description":"Free-form settings passed to the wrapper function at runtime (connector-specific keys such\nas method, apiVersion, headers, or handler). Structure is defined by the wrapper code, not\nby this schema."},"lookups":{"$ref":"#/components/schemas/Lookups","description":"Lookup definitions for this wrapper import. Wrapper lookups use\nthe generic Celigo shape: either a static `map` (key→value\nobject) or a dynamic `_lookupCacheId` + `extract` pair. See\n`common/schemas/lookups.yml` for the full item shape."}},"if":{"not":{"propertyNames":{"enum":["lookups"]}}},"then":{"required":["function"]}},"Salesforce":{"type":"object","description":"Salesforce-specific configuration for the import: the operation to perform, the API to use, and object-level settings such as `sObjectType`, `idLookup`, and `upsert`. Properties like `ignoreExisting`, `ignoreMissing`, `name`, and `description` are not part of this object — they belong at the import level.","properties":{"lookups":{"description":"Lookup definitions used to resolve Salesforce record references or\nstatic value mappings. Each lookup has a unique ``name`` that mapping\nfields reference via ``lookupName`` and that Handlebars expressions\nreference via ``{{lookup \"name\" value}}``.\n\n**Mirror invariant**\nThe Celigo platform stores lookups in TWO locations on a Salesforce\nimport resource: top-level ``lookups`` and ``salesforce.lookups``.\nBoth arrays must hold byte-for-byte identical content — the\nMappingsAgent writes both on every save.\n\n**Static vs dynamic lookups**\n- **Static** — only ``map`` + optional ``default``. No Salesforce\n  query runs. Use for small controlled vocabularies (country codes,\n  status labels, etc.).\n- **Dynamic** — ``sObjectType`` + ``resultField`` + ``whereClause``.\n  Issues a SOQL query at runtime. Use ``map`` alongside the query to\n  short-circuit known values before hitting Salesforce.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup entry. Mapping fields reference\nit via ``lookupName``, and Handlebars expressions reference it\nvia ``{{lookup \"name\" value}}``. Must be unique within this\nresource's ``lookups`` array. Conventionally a short slug or a\ngenerated hash — the value is only meaningful as a reference\ntoken.\n"},"sObjectType":{"type":"string","description":"Salesforce sObject API name to query (e.g. ``Account``,\n``Contact``, ``Solution__c``). For custom objects include the\n``__c`` suffix. Case-sensitive — must match exactly as declared\nin the target Salesforce org.\n"},"resultField":{"type":"string","description":"sObject field whose value is returned when the SOQL ``whereClause``\nmatches a record. Typically ``Id`` when the lookup resolves to a\nSalesforce record reference, but any queryable field is valid.\n"},"whereClause":{"type":"string","description":"SOQL ``WHERE`` clause that identifies the matching Salesforce\nrecord. Use Handlebars expressions (triple braces recommended:\n``{{{field}}}``) to inject values from the incoming source record.\nWrap the whole expression in parentheses.\n\nExamples:\n- ``(Id = {{{id [Customer SFDC ID]}}})``\n- ``(Email = '{{{email}}}')``\n- ``(External_Id__c = {{{string externalId}}} AND IsActive = true)``\n\nCeligo's Handlebars helpers (``string``, ``id``, ``double``,\n``bool``, ``date``) produce correctly-typed SOQL literals —\nprefer them over manual quoting. String values wrap in single\nquotes; numeric/id values do not.\n"},"map":{"type":["object","null"],"description":"Optional static key→value map evaluated BEFORE the SOQL query.\nWhen the incoming field value matches a key, the mapped value\nis returned without hitting Salesforce. Useful for small\ncontrolled enumerations where a full query would be overkill.\n"},"default":{"type":["string","null"],"description":"Value returned when the SOQL query matches no records. When\nomitted and ``allowFailures`` is false, an unmatched lookup\nfails the record.\n"},"allowFailures":{"type":"boolean","description":"When true, a lookup miss (no matching record and no ``default``)\nis tolerated — the mapped field resolves to ``null`` and the\nimport continues. When false (default), a miss halts processing\nfor the record.\n"},"useDefaultOnMultipleMatches":{"type":"boolean","description":"Controls behaviour when the SOQL query matches more than one\nrecord. When true, the ``default`` value is returned instead of\nraising an ambiguous-match error. When false (default), multiple\nmatches are treated as a failure.\n"}}},"type":"array"},"operation":{"type":"string","enum":["insert","update","upsert","upsertpicklistvalues","delete","addupdate"],"description":"Controls how records are written to Salesforce. Each value pairs with a lookup strategy: `upsert` requires `upsert.externalIdField` plus `idLookup.extract`, while `update`, `delete`, `addupdate`, and `insert` with `ignoreExisting` require `idLookup.whereClause`. Values are converted to lowercase automatically."},"api":{"type":"string","enum":["soap","rest","metadata","compositerecord"],"description":"Selects the Salesforce API used to execute the import. Use `soap` for most imports and another API only when its specific capability is needed; default to `soap` when no API is specified. Values are converted to lowercase automatically."},"soap":{"type":"object","properties":{"headers":{"type":"object","properties":{"allOrNone":{"type":"boolean","description":"When true, the batch is processed atomically: if any record fails, the entire transaction is rolled back and nothing is committed. When false or omitted, records that succeed are committed even if others in the batch fail."}},"description":"SOAP headers included in requests sent to Salesforce, such as the `allOrNone` transaction-control header."},"batchSize":{"type":"number","description":"Number of records sent to Salesforce in each SOAP API batch. Larger batches reduce the number of API calls; smaller batches reduce per-call load and the chance of timeouts."}},"description":"Settings that apply when `api` is `soap`, including SOAP request headers and batch size."},"sObjectType":{"type":"string","description":"Salesforce object the import writes to, such as `Account`, `Contact`, or a custom object like `Vendor__c`. Must exactly match the object's case-sensitive API name; custom objects include the `__c` suffix."},"idLookup":{"type":"object","properties":{"extract":{"type":"string","description":"Field in the incoming data whose value is matched against the Salesforce External ID field named in `upsert.externalIdField`. Set only when `operation` is `upsert`; all other operations use `whereClause` instead. Choose a field that is unique and always populated — missing or null values cause the upsert to fail. If both `extract` and `whereClause` are set, `extract` takes precedence for upsert."},"whereClause":{"type":"string","description":"SOQL condition (without the `WHERE` keyword) that finds existing Salesforce records for each incoming record. Required when `operation` is `update`, `delete`, or `addupdate`, and for `insert` with `ignoreExisting`; for `upsert`, use `extract` instead. Reference incoming fields with triple-brace Handlebars and wrap string values in single quotes (e.g. `Email = '{{{Email}}}'`) — triple braces emit the raw value without HTML-escaping, which would corrupt characters like `&` or `'`. Combine conditions with `AND`/`OR`."}},"description":"Controls how existing Salesforce records are matched before writing. Set exactly one of `extract` (for `upsert`) or `whereClause` (for `update`, `delete`, `addupdate`, and `insert` with `ignoreExisting`); omit for plain inserts.\n\nRequired when operation is addupdate, delete, update, or upsert."},"upsert":{"type":"object","properties":{"externalIdField":{"type":"string","description":"API name of the Salesforce field, marked as External ID, that upsert operations match against. This is the target side of the match; `idLookup.extract` names the source field in the incoming data. If the value matches multiple Salesforce records, the upsert fails."}},"description":"Upsert matching configuration, required when `operation` is `upsert` and omitted for all other operations. Contains only `externalIdField`, which pairs with `idLookup.extract` at the parent level.\n\nRequired when operation is upsert."},"upsertpicklistvalues":{"type":"object","properties":{"type":{"type":"string","enum":["picklist","multipicklist"],"description":"Picklist field type being managed. Values are converted to lowercase automatically."},"fullName":{"type":"string","description":"Fully qualified API name of the picklist field, in `ObjectName.FieldName__c` form (for example, `Account.MyPicklist__c`). Must match the case-sensitive Salesforce API name."},"label":{"type":"string","description":"Display label for the picklist field in the Salesforce UI. Can contain spaces and does not need to match the API name."},"visibleLines":{"type":"number","description":"Number of values visible without scrolling in the Salesforce UI selection box. Only applies when `type` is `multipicklist`."}},"description":"Picklist field definition managed when `operation` is `upsertpicklistvalues`; omit for all other operations. Manages the field's metadata — name, label, and display settings — and requires Salesforce Metadata API permissions.\n\nRequired when operation is upsertpicklistvalues."},"removeNonSubmittableFields":{"type":"boolean","description":"When true, strips fields Salesforce will not accept — read-only, system-generated, and formula fields — from the payload before submission, preventing rejection errors. When false or omitted, all fields are submitted as-is. Only the outgoing payload is affected; the source data is unchanged."},"document":{"type":"object","properties":{"id":{"type":"string","description":"Salesforce-assigned ID of the Document record. The 15-character form is case-sensitive; the 18-character form adds a case-insensitivity checksum."},"name":{"type":"string","description":"Display name of the document in Salesforce, used to identify it in folders, search, and API operations."},"folderId":{"type":"string","description":"Salesforce folder that stores the document. The document inherits the folder's sharing and visibility settings; changing this value moves the document to a different folder."},"contentType":{"type":"string","description":"MIME type of the document content, such as `application/pdf` or `image/png`."},"developerName":{"type":"string","description":"Unique API name used to reference the document in code, metadata, and integrations. Distinct from the display name; uses only letters, numbers, and underscores with no spaces."},"isInternalUseOnly":{"type":"boolean","description":"When true, marks the document for internal use only — it must not be shared with parties outside the organization."},"isPublic":{"type":"boolean","description":"When true, the document is accessible to all users; when false, access is limited to authorized users by Salesforce permissions and sharing settings."}},"description":"Salesforce Document record written by the import, combining the file content with metadata such as name, folder, and content type."},"attachment":{"type":"object","properties":{"id":{"type":"string","description":"Salesforce-assigned ID of the Attachment record. Prefer the 18-character case-insensitive form over the 15-character case-sensitive form in integrations."},"name":{"type":"string","description":"Filename of the attachment, including the file extension (for example, `contract_agreement.pdf`)."},"parentId":{"type":"string","description":"Salesforce record the attachment is linked to, such as an Account, Contact, or Opportunity. Required when creating an attachment; access to the attachment follows the parent record's permissions."},"contentType":{"type":"string","description":"MIME type of the attachment content, such as `application/pdf` or `image/png`."},"isPrivate":{"type":"boolean","description":"When true, the attachment is visible only to users explicitly authorized to access it; when false or omitted, it inherits the visibility of its parent record. This setting only restricts access further — it never grants access beyond the parent record's permissions."},"description":{"type":"string","description":"Free-text note describing the attachment's content or purpose, shown alongside the attachment details in Salesforce."}},"description":"Salesforce Attachment record written by the import — a file linked to a parent record via `parentId`, with metadata such as name and content type."},"contentVersion":{"type":"object","properties":{"contentDocumentId":{"type":"string","description":"ContentDocument this version belongs to; all versions of the same document share this ID. Assigned by Salesforce when the content version is created and immutable afterward."},"title":{"type":"string","description":"Display title of this content version, shown in content libraries, search results, and version history."},"pathOnClient":{"type":"string","description":"Original file path on the client machine before upload. Used for reference and display only; it does not affect how the file is stored or accessed in Salesforce."},"tagCsv":{"type":"string","description":"Comma-separated tags applied to the content version for categorization and search. Updating this field replaces the entire tag set, so include every tag the version should keep."},"contentLocation":{"type":"string","description":"Specifies where the content file for this ContentVersion record is stored — \"S\" for Salesforce internal storage or \"E\" for an external system (the two values the import form offers). Salesforce also defines \"L\" for content shared via a link, but the form does not set it. Typically assigned automatically based on the upload method."}},"description":"Salesforce ContentVersion record written by the import — a specific version of a content item in Salesforce Files. Each modification produces a new version, and all versions of a document are linked through `contentDocumentId`."}},"if":{"anyOf":[{"required":["attachment"]},{"required":["contentVersion"]}]},"then":{"required":["operation"]},"else":{"if":{"not":{"propertyNames":{"enum":["lookups","api"]}}},"then":{"required":["operation","api"],"if":{"properties":{"operation":{"const":"upsert"}},"required":["operation"]},"then":{"required":["idLookup","upsert"],"properties":{"idLookup":{"required":["extract"]},"upsert":{"required":["externalIdField"]}}},"else":{"if":{"properties":{"operation":{"enum":["update","delete","addupdate"]}},"required":["operation"]},"then":{"required":["idLookup"],"properties":{"idLookup":{"required":["whereClause"]}}},"else":{"if":{"properties":{"operation":{"const":"upsertpicklistvalues"}},"required":["operation"]},"then":{"required":["upsertpicklistvalues"],"properties":{"upsertpicklistvalues":{"required":["fullName"]}}}}}}}},"Tool":{"type":"object","description":"Configuration for a `ToolImport`, which executes a reusable Celigo Tool resource as the\nimport action. The referenced tool defines its own input contract, processing pipeline, and\noutput; this object binds the tool to the import and optionally overrides the connections the\ntool uses for this invocation.","required":["_toolId"],"properties":{"_toolId":{"type":"string","format":"objectId","description":"Tool resource this import executes. The tool supplies the input schema, routing, page\nprocessors, and output mapping; the import runs that tool when records reach it. Required —\na `ToolImport` has no behavior without a tool to run."},"overrides":{"type":"object","description":"Per-import overrides applied to the referenced tool at execution time. Tools use a\nbring-your-own-keys connection model: the tool declares abstract connection slots, and the\nconsumer binds concrete connections here so one tool definition can run against different\nsystems without being modified. Omit to run the tool with its default connections.","properties":{"connections":{"type":"array","description":"Connection bindings that replace the tool's abstract connection slots for this import.\nEach entry maps one of the tool's abstract connections to the concrete connection the\nimport should use.","items":{"type":"object","required":["_id","_abstractId"],"properties":{"_id":{"type":"string","format":"objectId","description":"Concrete connection bound to the abstract slot for this import's tool run."},"_abstractId":{"type":"string","format":"objectId","description":"Abstract connection slot, declared by the tool, that this binding replaces. Matches\nthe abstract connection id defined on the referenced tool."},"_borrowConcurrencyFromConnectionId":{"type":"string","format":"objectId","description":"Optional pointer to another connection whose\nconcurrency budget this binding shares. Use\nwhen several wrappers / steps should share rate\nlimits against the same downstream system but\nuse different credentials. Mutually exclusive\nwith `_id` on the same entry.\n"}}}}}}}},"File":{"type":"object","description":"**CRITICAL: This object is REQUIRED for all file-based import adaptor types.**\n\n**When to include this object**\n\n✅ **MUST SET** when `adaptorType` is one of:\n- `S3Import`\n- `FTPImport`\n- `AS2Import`\n- `HTTPImport` in file mode (`http.type: \"file\"` — the folder mode of the cloud file\n  providers such as Google Drive, Box, Dropbox, Azure Blob, Google Cloud Storage and Celigo\n  Storage); the same `file` object, including `type: filedefinition` with `_ediProfileId`\n  for EDI, applies as on S3 / FTP\n\n❌ **DO NOT SET** for non-file-based imports like:\n- `SalesforceImport`\n- `NetSuiteImport`\n- `HTTPImport` in records mode (`http.type` unset — a REST endpoint receiving records)\n- `MongodbImport`\n- `RDBMSImport`\n\n**Minimum required fields**\n\nFor most file imports, you need at minimum:\n- `fileName`: The output file name (supports Handlebars like `{{timestamp}}`)\n- `type`: The file format (json, csv, xml, xlsx)\n- `aggregation.type`: How the run's records are batched into files (`all` for one file per run)\n\n**Example (S3 Import)**\n\n```json\n{\n  \"file\": {\n    \"fileName\": \"customers-{{timestamp}}.json\",\n    \"aggregation\": { \"type\": \"all\" },\n    \"type\": \"json\"\n  },\n  \"s3\": {\n    \"region\": \"us-east-1\",\n    \"bucket\": \"my-bucket\",\n    \"fileKey\": \"customers-{{timestamp}}.json\"\n  },\n  \"adaptorType\": \"S3Import\"\n}\n```","properties":{"fileName":{"type":"string","description":"**REQUIRED for file-based imports.**\n\nThe name of the file to be created/written. Supports Handlebars expressions for dynamic naming.\n\n**Default behavior**\n- When the user does not specify a file naming convention, **always default to timestamped filenames** using `{{timestamp}}` (e.g., `\"items-{{timestamp}}.csv\"`). This ensures each run produces a unique file and avoids overwriting previous exports.\n- Only use a fixed filename (without timestamp) if the user explicitly requests overwriting or a fixed name.\n\n**Common patterns**\n- `\"data-{{timestamp}}.json\"` - Timestamped JSON file (DEFAULT — use this pattern when not specified)\n- `\"export-{{date}}.csv\"` - Date-stamped CSV file\n- `\"{{recordType}}-backup.xml\"` - Dynamic record type naming\n\n**Examples**\n- `\"customers-{{timestamp}}.json\"`\n- `\"orders-export.csv\"`\n- `\"inventory-{{date}}.xlsx\"`\n\n**Important**\n- For S3 imports, this should typically match the `s3.fileKey` value\n- For FTP imports, this should typically match the `ftp.fileName` value"},"skipAggregation":{"type":"boolean","description":"Legacy companion of `aggregation.type`: `true` when the run's records are written one\nfile per page, `false` when they are combined across the run or grouped by key.\nValidation reads the value sent — an EDI file definition needs `true` unless\n`aggregation.type` is `key`, and `batchSize` needs `true` — then the save rewrites it\nfrom `aggregation.type`, so send the two as a consistent pair.","default":false},"type":{"type":"string","enum":["json","csv","xml","xlsx","filedefinition"],"description":"**REQUIRED for file-based imports.**\n\nThe format of the output file.\n\nSelect the format that matches the target system's requirements."},"encoding":{"type":"string","enum":["utf8","win1252","utf-16le","gb18030","macroman","iso88591","shiftjis"],"description":"Character encoding for the output file.\n\n**Default:** `\"utf8\"`\n\nChange from the default only when the target system requires a specific encoding.","default":"utf8"},"delete":{"type":"boolean","description":"Whether to delete the source file after successful import.\n\n**Values**\n- `true`: Delete source file after processing\n- `false`: Keep source file\n\n**Default:** `false`","default":false},"compressionFormat":{"type":"string","enum":["gzip","zip"],"description":"Compression format for the output file.\n\n**When to use**\n- Large files that benefit from compression\n- When target system expects compressed files"},"backupPath":{"type":"string","description":"Path where backup copies of files should be stored.\n\n**Examples**\n- `\"backup/\"` - Relative backup folder\n- `\"/archive/2024/\"` - Absolute backup path"},"purgeInternalBackup":{"type":"boolean","description":"Whether to purge internal backup copies after successful processing.\n\n**Default:** `false`","default":false},"batchSize":{"type":"integer","description":"Number of records to include per batch/file when processing large datasets.\n\n**When to use**\n- Large imports that need to be split into multiple files\n- When target system has file size limitations\n\n**Note**\n- Offered when records are written one file per page (`aggregation.type: page` with\n  `skipAggregation: true`)"},"encrypt":{"type":"boolean","description":"Whether to encrypt the output file.\n\n**Values**\n- `true`: Encrypt the file (requires PGP configuration)\n- `false`: No encryption\n\n**Default:** `false`","default":false},"csv":{"type":"object","description":"CSV-specific configuration. Only used when `type` is `\"csv\"`.","properties":{"rowDelimiter":{"type":"string","description":"Character(s) used to separate rows. Default is newline.","default":"\n"},"columnDelimiter":{"type":"string","description":"Character(s) used to separate columns. Default is comma.","default":","},"includeHeader":{"type":"boolean","description":"When true, includes a header row with column names in the output.","default":true},"wrapWithQuotes":{"type":"boolean","description":"When true, wraps field values in quotes.","default":false},"replaceTabWithSpace":{"type":"boolean","description":"Replace tab characters with spaces.","default":false},"replaceNewlineWithSpace":{"type":"boolean","description":"Replace newline characters with spaces within fields.","default":false},"truncateLastRowDelimiter":{"type":"boolean","description":"Remove trailing row delimiter from the file.","default":false}}},"json":{"type":"object","description":"JSON-specific configuration. Only used when `type` is `\"json\"`.","properties":{"resourcePath":{"type":"string","description":"JSONPath expression to locate records within the JSON structure."}}},"xml":{"type":"object","description":"XML-specific configuration. Only used when `type` is `\"xml\"`.","properties":{"resourcePath":{"type":"string","description":"XPath expression to locate records within the XML structure."}}},"fileDefinition":{"type":"object","description":"Configuration settings for parsing files using a predefined file definition. This object enables processing of complex, non-standard, or proprietary file formats that require specialized parsing logic beyond what the standard parsers (CSV, JSON, XML, etc.) can handle.\n\n**When to use**\n\nConfigure this object when the `type` field is set to \"filedefinition\". This approach is required for properly handling:\n- Legacy or proprietary file formats with complex structures\n- Fixed-width text files where field positions are defined by character positions\n- Electronic Data Interchange (EDI) documents (X12, EDIFACT, etc.)\n- Multi-record type files where different lines have different formats\n- Files requiring complex preprocessing or custom parsing logic\n\n**File definition characteristics**\n\n- **Custom Parsing Rules**: Applies predefined parsing logic to complex file formats\n- **Reusable Configurations**: References externally defined parsing rules that can be reused\n- **Complex Format Support**: Handles formats that standard parsers cannot process\n- **Specialized Processing**: Often used for industry-specific or legacy formats\n\n**Implementation strategy for ai agents**\n\n1. **Format Analysis**:\n    - Determine if the file format is standard (CSV, JSON, XML) or requires custom parsing\n    - Check if the format follows industry standards like EDI, SWIFT, or fixed-width\n    - Assess if there are multiple record types within the same file\n    - Identify if specialized logic is needed to interpret the file structure\n\n2. **File Definition Selection**:\n    - Verify that a suitable file definition has already been created in the system\n    - Check if existing file definitions match the format requirements\n    - Confirm the file definition ID from system administrators if needed\n    - Ensure the file definition is compatible with the export's needs\n","properties":{"_fileDefinitionId":{"type":"string","format":"objectId","description":"Reference to the file definition resource."}}},"aggregation":{"type":"object","description":"Controls how a flow run's records are batched into output files — the transfer form's\n\"How many files would you like to generate?\" setting. The legacy `skipAggregation` flag\nmirrors `type` and is maintained by the platform.","properties":{"type":{"type":"string","enum":["all","page","key"],"description":"Which batches of a run's records become separate output files. EDI file definitions\n(`file.type: filedefinition`) are written per page or per key, never per run; `key`\nis accepted only for them and only when the account has B2B Manager. The grouping\nkey and the line-item loop it merges come from the EDI file definition's document\ntype, so there is no separate key field to configure."}}},"lookups":{"type":"array","description":"Named value-substitution maps available to this import's field mappings; each lookup\ntranslates source values to target values, with an optional default for unmatched values.\nReferenced by name from mapping rules.","items":{"type":"object","properties":{"name":{"type":"string","description":"Identifier used to reference this lookup from mapping rules."},"map":{"type":"object","description":"Source-value to target-value pairs applied during mapping.","additionalProperties":{"type":"string"}},"default":{"type":["string","null"],"description":"Value substituted when a source value is not found in map; empty string when omitted. May be stored as null (no default)."},"allowFailures":{"type":"boolean","description":"When true, a value missing from map does not fail the record and the default (or original value) is used."}}}},"pgp":{"type":"object","description":"PGP encryption settings applied when encrypt is true; selects the symmetric cipher and the\nsigning hash. The connection must hold the recipient's public key.","properties":{"symmetricKeyAlgorithm":{"type":"string","enum":["twofish","cast5","3des","aes128","aes192","aes256"],"default":"aes256","description":"Symmetric cipher used to encrypt the file body; aes256 unless the recipient requires another."},"hashAlgorithm":{"type":"string","enum":["sha256","sha384","sha512","sha224"],"description":"Hash algorithm used when signing the encrypted file; set only when the recipient requires a specific one."}}},"skipRename":{"type":"boolean","description":"When true, the import writes directly to the final file name instead of writing to a\ntemporary name and renaming on completion. Use only when the destination does not support\natomic rename; the default (false) uses the safe write-then-rename behavior."},"directory":{"type":"object","required":["pathMode"],"description":"Structured destination location for cloud file-provider imports (Google Drive shared\ndrives, Box, Dropbox), replacing the flat path used by classic FTP/S3 destinations. Select\nthe location by folder ID or by a path relative to a configured storage root via pathMode.\nFor these cloud providers, directoryId mode addresses the folder by id alone — the flat\ndestination-path field is not consulted at runtime.","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 destination directory (e.g. a Google Drive folder ID);\nset when pathMode is directoryId. This is the file provider's own ID, not a Celigo resource ID."},"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."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root the relative path resolves against; set when\npathMode is relativePath. The provider's own ID, not a Celigo resource ID."},"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 location for backup copies on cloud file providers — the file-provider\ncounterpart 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."}}}}},"FileSystem":{"type":"object","description":"Defines where files are written to 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 where generated files are written; the agent's OS\naccount must have write permission on it. Accepts a local OS path or a UNC network share,\nand supports handlebars templates for dynamic folders."}}},"AiAgentConfig":{"type":"object","description":"AI Agent configuration used by both AiAgentImport and GuardrailImport (ai_agent type).\n\nConfigures which AI provider and model to use, along with instructions, parameter\ntuning, output format, and available tools. Providers come in two families with\ntwo configuration formats:\n\n- **Built-in providers** — each has its own configuration block.\n  - **openai**: OpenAI models (GPT-4.1, GPT-5, etc.). Configure via the `openai` object.\n  - **gemini**: Google Gemini models via the LiteLLM proxy. Configure via `litellm` with overrides in `litellm._overrides.gemini`.\n  - **anthropic**: Anthropic Claude models via the LiteLLM proxy. Configure via `litellm` with overrides in `litellm._overrides.anthropic`.\n- **Catalog providers** — `mistral`, `xai`, `huggingface`, `deepseek`, `cohere`, `groq`. Added\n  as catalog data rather than platform code, they share one generic flat format:\n  `model`, `modelOptions`, `instructions`, `output`, and the `tools` / `prompts` /\n  `resources` arrays directly on this object.\n\nA `_connectionId` on the parent import selects bring-your-own-key (BYOK)\ncredentials. Without one, built-in providers run on platform-managed\ncredentials; catalog providers have no platform-managed credentials, so they\nsave without a connection but cannot run until one is attached.\n","required":["provider"],"properties":{"provider":{"type":"string","enum":["openai","gemini","anthropic","mistral","xai","huggingface","deepseek","cohere","groq"],"description":"AI provider to use."},"model":{"type":"string","description":"Model identifier for a catalog provider (generic flat format) — for example\n`grok-4.6`. Built-in providers set the model inside their own block\n(`openai.model`, `litellm.model`) instead."},"modelOptions":{"type":"object","description":"Model tuning for a catalog provider (generic flat format). Which keys, values, and\nranges are accepted is defined per catalog model and validated on save when the\nmodel is a catalog entry.","properties":{"maxOutputTokens":{"type":"number","description":"Maximum number of tokens the model may generate."},"temperature":{"type":"number","description":"Sampling temperature."},"topP":{"type":"number","description":"Nucleus sampling threshold."},"reasoning_effort":{"type":"string","enum":["low","medium","high","xhigh"],"description":"Reasoning depth for models that expose it."}}},"instructions":{"type":"string","description":"System prompt for a catalog provider (generic flat format). Built-in providers\nset it inside their own block (`openai.instructions`,\n`litellm._overrides.anthropic.systemInstruction`)."},"output":{"type":"object","description":"Output format for a catalog provider (generic flat format). Built-in providers\nconfigure it inside their own block (`openai.output`, `litellm.responseFormat`).","properties":{"format":{"type":"object","description":"Controls the structure of the model's output.","properties":{"type":{"type":"string","enum":["text","json_schema"],"description":"Output type."},"name":{"type":"string","description":"Name of the JSON schema, for `json_schema` output."},"strict":{"type":"boolean","description":"When true, the model must conform exactly to `jsonSchema`."},"jsonSchema":{"type":"object","additionalProperties":true,"description":"JSON Schema the structured output must conform to, for `json_schema` output."}}},"verbose":{"type":"string","description":"Level of detail in the model's response, for models that expose it."}}},"tools":{"type":"array","description":"Tools available to a catalog-provider agent (generic flat format). Entries mirror\nthe built-in providers' tool entries: a Celigo Tool by `_toolId`, an MCP server by\n`_mcpConnectionId`, or a vendor web-search tool.","items":{"type":"object","properties":{"type":{"type":"string","enum":["tool","mcp","web_search"],"description":"Type of tool entry."},"tool":{"type":"object","description":"Reference to a Celigo Tool resource, used when type is \"tool\".","properties":{"_toolId":{"type":"string","format":"objectId","description":"The Celigo Tool to call."}}},"mcp":{"type":"object","description":"MCP server connection, used when type is \"mcp\".","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if omitted). Each entry\nis either a plain tool name or an object carrying display metadata.","items":{"type":["string","object"]}}}},"config":{"type":"object","additionalProperties":true,"description":"Tool-specific options for vendor-native tools (for example web-search settings)."}}}},"prompts":{"type":"array","description":"MCP prompt entries available to a catalog-provider agent (generic flat format).\nEach entry references one MCP connection and the prompt names allowed from it.","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server that exposes the prompts."},"allowedPrompts":{"type":"array","description":"Prompt names the agent may fetch from the server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"},"openai":{"type":"object","description":"OpenAI-specific configuration. Used when `provider` is \"openai\".\n","required":["model","instructions"],"properties":{"instructions":{"type":"string","maxLength":1000000,"description":"System prompt that defines the AI agent's behavior, goals, and constraints.\n"},"model":{"type":"string","description":"OpenAI model identifier. Open string (not an enum) — model names change frequently."},"reasoning":{"type":"object","description":"Controls depth of reasoning for complex tasks.","properties":{"effort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh"],"description":"How much reasoning effort the model should invest"},"summary":{"type":"string","enum":["concise","auto","detailed"],"description":"Level of detail in reasoning summaries"}}},"temperature":{"type":"number","minimum":0,"maximum":2,"description":"Sampling temperature. Higher values (e.g. 1.5) produce more creative output,\nlower values (e.g. 0.2) produce more focused and deterministic output.\n"},"topP":{"type":"number","minimum":0.1,"maximum":1,"description":"Nucleus sampling parameter"},"topLogprobs":{"type":"number","minimum":0,"maximum":20,"description":"Number of most likely tokens to return log probabilities for at each output position."},"maxOutputTokens":{"type":"number","minimum":100,"maximum":128000,"default":5000,"description":"Maximum number of tokens in the model's response (server default observed live on create)"},"serviceTier":{"type":"string","enum":["auto","default","priority"],"default":"default","description":"OpenAI service tier. \"priority\" provides higher rate limits and\nlower latency at increased cost."},"output":{"type":"object","description":"Output format configuration","properties":{"format":{"type":"object","description":"Controls the structure of the model's output.\n","properties":{"type":{"type":"string","enum":["text","json_schema","blob"],"default":"text","description":"Output format type."},"schemaMode":{"type":"string","enum":["manual","json"],"description":"How the structured-output schema was authored in the UI.\nEditor state only — it does not change how `jsonSchema` is\nsent to the provider."},"name":{"type":"string","description":"Name for the output format (used with json_schema)"},"strict":{"type":"boolean","default":false,"description":"When true, enforces strict schema validation on output."},"jsonSchema":{"type":"object","description":"JSON Schema for structured output. Required when `format.type` is \"json_schema\".\n","properties":{"type":{"type":"string","description":"Root JSON Schema type of the structured output; use \"object\" for record-shaped results.","enum":["object","array","string","number","integer","boolean"]},"properties":{"type":"object","additionalProperties":true,"description":"JSON Schema definitions for each field the structured output may contain."},"required":{"type":"array","description":"Property names the model must include in the structured output.","items":{"type":"string"}},"additionalproperties":{"type":"boolean","description":"When true, the structured output may include properties beyond those defined in `properties`."}}}},"if":{"properties":{"type":{"const":"json_schema"}},"required":["type"]},"then":{"required":["name","jsonSchema"]}},"verbose":{"type":"string","enum":["low","medium","high"],"default":"medium","description":"Level of detail in the model's response"}}},"tools":{"type":"array","description":"Tools available to the AI agent during processing.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["web_search","mcp","image_generation","tool"],"description":"Type of tool."},"webSearch":{"type":"object","description":"Web search configuration (empty object to enable)"},"imageGeneration":{"type":"object","description":"Image generation configuration","properties":{"background":{"type":"string","description":"Controls whether generated images have a transparent or opaque background; use transparent only with output formats that support it (png, webp).","enum":["transparent","opaque"]},"quality":{"type":"string","description":"Rendering quality of generated images, trading detail for generation speed and file size.","enum":["low","medium","high"]},"size":{"type":"string","description":"Pixel dimensions of generated images; choose square, portrait, or landscape to match the intended use.","enum":["1024x1024","1024x1536","1536x1024"]},"outputFormat":{"type":"string","description":"File format of generated images; use png or webp when transparency is needed.","enum":["png","webp","jpeg"]}}},"mcp":{"type":"object","description":"MCP server tool configuration","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server"},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if\nomitted). Each entry is either a plain tool name (legacy\nform) or an object carrying display metadata.","items":{"type":["string","object"],"properties":{"name":{"type":"string","maxLength":256,"description":"Tool name as exposed by the MCP server."},"title":{"type":"string","maxLength":300,"description":"Display title shown for the tool."},"description":{"type":"string","maxLength":1000,"description":"Display description shown for the tool."}},"required":["name"]}},"allowedPrompts":{"type":"array","description":"Specific prompts to allow from the MCP server (used for MCP prompt entries; all if omitted).","items":{"type":"string"}}}},"tool":{"type":"object","description":"Reference to a Celigo Tool resource.\n","properties":{"_toolId":{"type":"string","format":"objectId","description":"Reference to the Tool resource"},"overrides":{"type":"object","description":"Per-agent overrides for the tool's internal resources","properties":{"connections":{"type":"array","description":"Remaps the tool's abstract connections for this agent. Each entry pairs\nthe tool's abstract connection placeholder (`_abstractId`) with the\nconcrete connection (`_id`) to use for this agent; entries without\n`_id` keep the tool's own default connection.\n","items":{"type":["object","null"],"required":["_abstractId"],"properties":{"_abstractId":{"type":"string","format":"objectId","description":"The tool's abstract connection placeholder being overridden."},"_id":{"type":"string","format":"objectId","description":"Concrete connection to use in place of the abstract placeholder."}}}}}}}}}}},"prompts":{"type":"array","description":"MCP prompt entries available to the agent. Each item references one MCP connection\nand the prompt names allowed from it. Configured alongside `tools` in the form but\nstored separately; an entry's `allowedPrompts` is what distinguishes a prompt entry\nfrom an MCP tool entry (which carries `allowedTools`).\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","description":"MCP server prompt configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedPrompts":{"type":"array","description":"Prompt names to allow from the MCP server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"}}},"litellm":{"type":"object","description":"LiteLLM proxy configuration. Used when `provider` is \"gemini\" or \"anthropic\".\n\nLiteLLM provides a unified interface to multiple AI providers. Gemini-specific\nsettings are in `_overrides.gemini`; Claude-specific settings are in\n`_overrides.anthropic`.\n\n`model` is required when litellm is the active provider path.\n","properties":{"model":{"type":"string","description":"LiteLLM model identifier. For Gemini, models are stored without the `gemini/`\nprefix; for Anthropic, use the Claude model id (e.g. `claude-sonnet-4-6`)."},"temperature":{"type":"number","minimum":0,"maximum":2,"description":"Sampling temperature"},"maxCompletionTokens":{"type":"number","minimum":100,"maximum":128000,"default":5000,"description":"Maximum number of tokens in the response"},"topP":{"type":"number","minimum":0.1,"maximum":1,"description":"Nucleus sampling parameter"},"seed":{"type":"number","description":"Random seed for reproducible outputs"},"responseFormat":{"type":"object","description":"Output format configuration","properties":{"type":{"type":"string","description":"Output format type.","enum":["text","json_schema","blob"],"default":"text"},"schemaMode":{"type":"string","enum":["manual","json"],"description":"How the structured-output schema was authored in the UI. Editor\nstate only — it does not change how `jsonSchema` is sent to the\nprovider."},"name":{"type":"string","description":"Name for the output format (used with json_schema)."},"strict":{"type":"boolean","description":"When true, enforces strict schema validation on output.","default":false},"jsonSchema":{"type":"object","description":"JSON Schema for structured output. Required when `responseFormat.type` is \"json_schema\".","properties":{"type":{"type":"string","description":"Root JSON Schema type of the structured output; use \"object\" for record-shaped results.","enum":["object","array","string","number","integer","boolean"]},"properties":{"type":"object","additionalProperties":true,"description":"JSON Schema definitions for each field the structured output may contain."},"required":{"type":"array","description":"Property names the model must include in the structured output.","items":{"type":"string"}},"additionalProperties":{"type":"boolean","description":"When true, the structured output may include properties beyond those defined in `properties`."}}}},"if":{"properties":{"type":{"const":"json_schema"}},"required":["type"]},"then":{"required":["name","jsonSchema"]}},"_overrides":{"type":"object","description":"Provider-specific overrides","properties":{"gemini":{"type":"object","description":"Gemini-specific configuration overrides.\n","required":["systemInstruction"],"properties":{"systemInstruction":{"type":"string","maxLength":1000000,"description":"System instruction for Gemini models. Equivalent to OpenAI's `instructions`.\nMaximum 1,000,000 characters.\n"},"tools":{"type":"array","description":"Gemini-specific tools","items":{"type":"object","properties":{"type":{"type":"string","enum":["googleSearch","urlContext","fileSearch","mcp","tool"],"description":"Type of Gemini tool."},"googleSearch":{"type":"object","description":"Google Search configuration (empty object to enable)"},"urlContext":{"type":"object","description":"URL context configuration (empty object to enable)"},"fileSearch":{"type":"object","description":"File search configuration, used when type is \"fileSearch\".","properties":{"fileSearchStoreNames":{"type":"array","description":"Names of the file search stores the model can query.","items":{"type":"string"}}}},"mcp":{"type":"object","description":"MCP server tool configuration, used when type is \"mcp\".","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if\nomitted). Each entry is either a plain tool name\n(legacy form) or an object carrying display metadata\n— same contract as the OpenAI `allowedTools`.","items":{"type":["string","object"],"properties":{"name":{"type":"string","maxLength":256,"description":"Tool name as exposed by the MCP server."},"title":{"type":"string","maxLength":300,"description":"Display title shown for the tool."},"description":{"type":"string","maxLength":1000,"description":"Display description shown for the tool."}},"required":["name"]}},"allowedPrompts":{"type":"array","description":"Specific prompts to allow from the MCP server (used for MCP prompt entries; all if omitted).","items":{"type":"string"}}}},"tool":{"type":"object","description":"Reference to a Celigo Tool resource, used when type is \"tool\".","properties":{"_toolId":{"type":"string","format":"objectId","description":"Reference to the Tool resource."},"overrides":{"type":"object","description":"Per-agent overrides for the tool's internal resources.","properties":{"connections":{"type":"array","description":"Remaps the tool's abstract connections for this agent. Each entry\npairs the tool's abstract connection placeholder (`_abstractId`)\nwith the concrete connection (`_id`) to use for this agent;\nentries without `_id` keep the tool's own default connection.\n","items":{"type":["object","null"],"required":["_abstractId"],"properties":{"_abstractId":{"type":"string","format":"objectId","description":"The tool's abstract connection placeholder being overridden."},"_id":{"type":"string","format":"objectId","description":"Concrete connection to use in place of the abstract placeholder."}}}}}}}}}}},"prompts":{"type":"array","description":"MCP prompt entries available to the Gemini agent. Each item references one\nMCP connection and the prompt names allowed from it. The presence of\n`allowedPrompts` distinguishes a prompt entry from an MCP tool entry.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","description":"MCP server prompt configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedPrompts":{"type":"array","description":"Prompt names to allow from the MCP server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"},"responseModalities":{"type":"array","description":"Response output modalities","items":{"type":"string","enum":["text","image"]},"default":["text"]},"topK":{"type":"number","description":"Top-K sampling parameter for Gemini"},"thinkingConfig":{"type":"object","description":"Controls Gemini's extended thinking capabilities","properties":{"includeThoughts":{"type":"boolean","description":"When true, includes the model's thinking steps in the response."},"thinkingBudget":{"type":"number","minimum":100,"maximum":4000,"description":"Maximum tokens allocated for thinking"},"thinkingLevel":{"type":"string","description":"Controls how much thinking effort the model applies; use higher levels for complex, multi-step tasks at the cost of latency and tokens.","enum":["minimal","low","medium","high"]}}},"imageConfig":{"type":"object","description":"Gemini image generation configuration","properties":{"aspectRatio":{"type":"string","description":"Aspect ratio of generated images; choose a ratio matching the intended display format.","enum":["1:1","2:3","3:2","3:4","4:3","4:5","5:4","9:16","16:9","21:9"]},"imageSize":{"type":"string","description":"Output resolution of generated images; higher resolutions increase detail and file size.","enum":["1K","2K","4K"]}}},"mediaResolution":{"type":"string","enum":["low","medium","high"],"description":"Resolution for media inputs (images, video)"}}},"anthropic":{"type":"object","description":"Claude-specific configuration overrides. Used when `provider` is \"anthropic\".\n","required":["systemInstruction"],"properties":{"systemInstruction":{"type":"string","maxLength":1000000,"description":"System instruction for Claude models. Equivalent to OpenAI's `instructions`.\nMaximum 1,000,000 characters.\n"},"topK":{"type":"number","minimum":0,"description":"Top-K sampling parameter. Deprecated on Claude models released after Claude Opus\n4.6, which reject any value at runtime; set it only on older models."},"thinkingConfig":{"type":"object","description":"Controls Claude's extended thinking capabilities.","properties":{"type":{"type":"string","enum":["enabled","disabled","adaptive"],"default":"disabled","description":"Extended thinking mode."},"budgetTokens":{"type":"number","minimum":1024,"description":"Maximum tokens allocated for thinking. Required when `type` is \"enabled\" —\nomitting it fails the save with 422 `invalid_thinking_config`."},"display":{"type":"string","enum":["summarized","omitted"],"default":"summarized","description":"How thinking output is surfaced in the response."},"effort":{"type":"string","enum":["low","medium","high","xhigh","max"],"description":"How much thinking effort the model applies; use with `type` \"adaptive\". Higher\nvalues (`xhigh`, `max`) may be gated to specific Claude models by the provider."}},"if":{"properties":{"type":{"const":"enabled"}},"required":["type"]},"then":{"required":["budgetTokens"]}},"serviceTier":{"type":"string","enum":["auto","standard_only"],"default":"auto","description":"Anthropic service tier for the request."},"tools":{"type":"array","description":"Claude-specific tools.","items":{"type":"object","properties":{"type":{"type":"string","enum":["tool","mcp","webSearch"],"description":"Type of Claude tool."},"tool":{"type":"object","description":"Reference to a Celigo Tool resource, used when type is \"tool\".","properties":{"_toolId":{"type":"string","format":"objectId","description":"Reference to the Tool resource."},"overrides":{"type":"object","description":"Per-agent overrides for the tool's internal resources.","properties":{"connections":{"type":"array","description":"Remaps the tool's abstract connections for this agent. Each entry\npairs the tool's abstract connection placeholder (`_abstractId`)\nwith the concrete connection (`_id`) to use for this agent;\nentries without `_id` keep the tool's own default connection.\n","items":{"type":["object","null"],"required":["_abstractId"],"properties":{"_abstractId":{"type":"string","format":"objectId","description":"The tool's abstract connection placeholder being overridden."},"_id":{"type":"string","format":"objectId","description":"Concrete connection to use in place of the abstract placeholder."}}}}}}}},"mcp":{"type":"object","description":"MCP server tool configuration, used when type is \"mcp\".","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if omitted).","items":{"type":"string"}}}},"webSearch":{"type":"object","description":"Web search configuration, used when type is \"webSearch\".","properties":{"version":{"type":"string","pattern":"^\\d{8}$","description":"Anthropic web search tool version (YYYYMMDD). Selects the tool version sent\non the wire; unsupported versions surface as an Anthropic 400."},"allowedDomains":{"type":"array","description":"Domains the search may return results from. Mutually exclusive with `blockedDomains`.","items":{"type":"string"}},"blockedDomains":{"type":"array","description":"Domains to exclude from search results. Mutually exclusive with `allowedDomains`.","items":{"type":"string"}},"userLocation":{"type":"object","description":"Approximate user location used to localize search results. When present, at\nleast one of `city`, `country`, `region`, or `timezone` must be set.","properties":{"type":{"type":"string","enum":["approximate"],"description":"Location type. Always \"approximate\"."},"city":{"type":"string","maxLength":256,"description":"City name for localizing search results."},"country":{"type":"string","maxLength":8,"description":"ISO 3166-1 alpha-2 country code for localizing search results."},"region":{"type":"string","maxLength":256,"description":"Region or state for localizing search results."},"timezone":{"type":"string","maxLength":64,"description":"IANA timezone for localizing search results."}}}}}}}},"prompts":{"type":"array","description":"MCP prompt entries available to the Claude agent. Each item references one MCP\nconnection and the prompt names allowed from it. The presence of `allowedPrompts`\ndistinguishes a prompt entry from an MCP tool entry.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","description":"MCP server prompt configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedPrompts":{"type":"array","description":"Prompt names to allow from the MCP server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"}}}}}}}},"if":{"properties":{"provider":{"const":"openai"}},"required":["provider"]},"then":{"required":["openai"],"properties":{"openai":{"required":["model","instructions"]}}},"else":{"if":{"properties":{"provider":{"enum":["gemini","anthropic"]}},"required":["provider"]},"then":{"required":["litellm"],"properties":{"litellm":{"required":["model"]}}},"else":{"if":{"properties":{"provider":{"enum":["mistral","xai","huggingface","deepseek","cohere","groq"]}},"required":["provider"]},"then":{"required":["model"]}}}},"McpResources":{"type":"array","description":"Governed MCP resources — read-only reference content (policies, schemas, documentation)\npulled from connected MCP servers and made available to the agent as a consistent source\nof truth. Each entry references one MCP connection and the specific resources allowed from it.","items":{"type":"object","required":["type","mcp"],"properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of resource entry. Always \"mcp\"."},"mcp":{"type":"object","required":["_mcpConnectionId","allowedResources"],"description":"MCP server resource configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server that exposes the resources."},"allowedResources":{"type":"array","minItems":1,"description":"Resources to expose to the agent from the MCP server. Must contain at least one\nentry; each entry identifies one resource by name and URI.","items":{"type":"object","required":["name","uri"],"properties":{"name":{"type":"string","description":"Display name of the MCP resource."},"uri":{"type":"string","description":"URI that identifies the resource on the MCP server."}}}}}}}}},"GuardrailConfig":{"type":"object","description":"Configuration for GuardrailImport adaptor type.\n\nGuardrails evaluate data flowing through integrations for safety and\ncompliance. The `type` field selects which check to apply, and the\ncorresponding sub-object (`aiAgent`, `pii`, or `moderation`) provides\nthe configuration.\n\nA `_connectionId` on the parent import is only needed for BYOK\n`ai_agent` guardrails. In responses the server echoes the active type's\nsub-object and applies the `confidenceThreshold` default; it also\nreturns inactive sibling sub-objects (e.g. `moderation: {categories: []}`\non a `pii` guardrail, or a populated `pii` left over from a type switch),\nbut only the active type's sub-object is meaningful. Legacy documents\nmay carry a server-written default `aiAgent` stub on `pii`/`moderation`\nguardrails; current servers strip the inactive `aiAgent` on write.","properties":{"type":{"type":"string","enum":["ai_agent","pii","moderation"],"description":"The type of guardrail to apply. Each type requires its corresponding\nsub-configuration object (`aiAgent`, `pii`, or `moderation`)."},"confidenceThreshold":{"type":"number","minimum":0,"maximum":1,"default":0.7,"description":"Confidence threshold (0 to 1). Detections below this threshold are\nignored. Lower values catch more issues but increase false positives."},"aiAgent":{"type":"object","description":"AI agent check configuration; set when `type` is `ai_agent`. On\n`pii`/`moderation` guardrails a legacy server-written stub may\nappear here — it is inert, and current servers strip it on write."},"pii":{"type":"object","required":["entities"],"description":"PII detection configuration. Required when `type` is `pii`.","properties":{"entities":{"type":"array","description":"PII entity types to detect. When `type` is `pii`, at least one\nentry is required; the inactive sibling on other guardrail types\nmay be served with an empty list.","items":{"type":"string","enum":["credit_card_number","card_security_code_cvv_cvc","cryptocurrency_wallet_address","date_and_time","email_address","iban_code","bic_swift_bank_identifier_code","ip_address","location","medical_license_number","national_registration_number","persons_name","phone_number","url","us_bank_account_number","us_drivers_license","us_itin","us_passport_number","us_social_security_number","uk_nhs_number","uk_national_insurance_number","spanish_nif","spanish_nie","italian_fiscal_code","italian_drivers_license","italian_vat_code","italian_passport","italian_identity_card","polish_pesel","finnish_personal_identity_code","singapore_nric_fin","singapore_uen","australian_abn","australian_acn","australian_tfn","australian_medicare","indian_pan","indian_aadhaar","indian_vehicle_registration","indian_voter_id","indian_passport","korean_resident_registration_number"]}},"mask":{"type":"boolean","default":false,"description":"When true, detected PII is replaced with masked values.\nWhen false, PII is flagged without modification."}}},"moderation":{"type":"object","required":["categories"],"description":"Content moderation configuration. Required when `type` is `moderation`.","properties":{"categories":{"type":"array","description":"Content moderation categories to check. When `type` is\n`moderation`, at least one entry is required; the inactive\nsibling on other guardrail types may be served with an empty\nlist.","items":{"type":"string","enum":["sexual","sexual_minors","hate","hate_threatening","harassment","harassment_threatening","self_harm","self_harm_intent","self_harm_instructions","violence","violence_graphic","illicit","illicit_violent"]}}}}},"required":["type","confidenceThreshold"],"if":{"required":["type"],"properties":{"type":{"const":"pii"}}},"then":{"required":["pii"],"properties":{"pii":{"required":["entities"],"properties":{"entities":{"minItems":1}}}}},"else":{"if":{"required":["type"],"properties":{"type":{"const":"moderation"}}},"then":{"required":["moderation"],"properties":{"moderation":{"required":["categories"],"properties":{"categories":{"minItems":1}}}}},"else":{"required":["aiAgent"],"properties":{"aiAgent":{"$ref":"#/components/schemas/AiAgentConfig"}}}}},"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"},"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\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**: Using wildcards like `*` and `?`\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- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"isempty\"`: Field is empty or null (is empty)\n- `\"isnotempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches specified pattern (matches)\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- Returns the raw field value which may need type conversion\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  - `export.settingName`: Access export-level settings\n  - `import.settingName`: Access import-level settings\n  - `integration.settingName`: Access integration-level settings\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\nType conversion operators are often necessary when comparing extracted field values against literals or when the field type doesn't match the comparison operator's expected type. 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    \"extract\",\n    \"status\"\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"extract\",\n    \"filename\"\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\n```json\n[\n  \"greaterthan\",\n  [\n    \"extract\",\n    \"orderDate\"\n  ],\n  \"2023-01-01T00:00:00Z\"\n]\n```\n\nExample 5: Category contains any of [\"Urgent\", \"High Priority\"]\n```json\n[\n  \"anyof\",\n  [\n    \"extract\",\n    \"category\"\n  ],\n  [\"Urgent\", \"High Priority\"]\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 that exists in the system.\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"}}}}},"Hook":{"type":"object","description":"A single lifecycle hook — a JavaScript function invoked from a script (or hosted on a\nstack) at a fixed point in an import or AI-agent run. Shared by every hook slot; the\nslot's own description says when in the lifecycle it fires.","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","null"],"description":"Static parameters passed to the hook function at runtime, letting one script be\nreused with different settings. Null when the hook carries no static parameters."}}},"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.\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"}}}}},"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, arrays of objects, arrays of arrays of objects) are ALL\n   handled through this recursive pattern\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","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- For arrays within arrays: Leave EMPTY for the inner array mappings, as they don't have field names\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, empty 'generate' fields almost always indicate inner array structures\nwithin a parent array.\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, attempts conversion from strings\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. REQUIRES child mappings in the 'mappings' array\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- `arrayarray`: Array of arrays (for matrix/table structures)\n\nArray dataTypes can be populated two ways: pass a source array through\nunchanged via `extract` alone (when the source is already an array of\nthe right shape), or construct/iterate via `buildArrayHelper`.\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` is one option (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","arrayarray"]},"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\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 `object`, `objectarray`, and primitive-array dataTypes as\n  well as scalars — the rendered template output must parse into the\n  declared shape\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- When both extract and mappings are defined for objects, extract is applied first\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 empty (the UI's \"Do nothing\" action).\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.\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"},"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, date)\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 REQUIRED mechanism for ALL array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.)\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**For Arrays of Arrays (arrayarray)**\n- Similar to objectarray, but inner arrays have empty 'generate' fields\n- Used for matrix/table structures\n```json\n{\n  \"generate\": \"matrix\",\n  \"dataType\": \"arrayarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.rows[*]\",  // For each row in the rows array\n      \"mappings\": [\n        {\n          \"dataType\": \"numberarray\",  // Note: No generate field for inner arrays\n          \"buildArrayHelper\": [\n            {\"extract\": \"$.rows.columns[*]\"}  // Notice: \"rows\" is singular in the composite object\n          ]\n        }\n      ]\n    }\n  ]\n}\n```\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"},"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":"Specifies the condition that must be met for an array element to be included.\n\n'extract_not_empty' only includes elements where the extract field returns a value.\n","enum":["extract_not_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"}}}}},"status":{"type":"string","description":"**Purpose**\nRequired on every mapping entry. Controls whether the mapping is applied.\n\n**Guidance**\nEmit `\"Active\"` for mappings that should run; `\"Draft\"` saves an\nin-progress mapping without the Active-only field validations. The\nAPI rejects a mapping missing this field\n(validation error: \"Mapping object must have status field present.\").\n","enum":["Active","Draft"]},"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**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":"Specifies the condition that triggers application of this mapping:\n- record_created: Apply only when creating a new record\n- record_updated: Apply only when updating an existing record\n- extract_not_empty: Apply only when the extract field returns a value\n","enum":["record_created","record_updated","extract_not_empty"]}}}},"required":["dataType"]}},"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- For arrays within arrays: Leave EMPTY for the inner array mappings, as they don't have field names\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, empty 'generate' fields almost always indicate inner array structures\nwithin a parent array.\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, attempts conversion from strings\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. REQUIRES child mappings in the 'mappings' array\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- `arrayarray`: Array of arrays (for matrix/table structures)\n\nArray dataTypes can be populated two ways: pass a source array through\nunchanged via `extract` alone (when the source is already an array of\nthe right shape), or construct/iterate via `buildArrayHelper`.\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` is one option (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","arrayarray"]},"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\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 `object`, `objectarray`, and primitive-array dataTypes as\n  well as scalars — the rendered template output must parse into the\n  declared shape\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- When both extract and mappings are defined for objects, extract is applied first\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 empty (the UI's \"Do nothing\" action).\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.\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"},"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, date)\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 REQUIRED mechanism for ALL array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.)\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**For Arrays of Arrays (arrayarray)**\n- Similar to objectarray, but inner arrays have empty 'generate' fields\n- Used for matrix/table structures\n```json\n{\n  \"generate\": \"matrix\",\n  \"dataType\": \"arrayarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.rows[*]\",  // For each row in the rows array\n      \"mappings\": [\n        {\n          \"dataType\": \"numberarray\",  // Note: No generate field for inner arrays\n          \"buildArrayHelper\": [\n            {\"extract\": \"$.rows.columns[*]\"}  // Notice: \"rows\" is singular in the composite object\n          ]\n        }\n      ]\n    }\n  ]\n}\n```\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"},"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":"Specifies the condition that must be met for an array element to be included.\n\n'extract_not_empty' only includes elements where the extract field returns a value.\n","enum":["extract_not_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"}}}}},"status":{"type":"string","description":"**Purpose**\nRequired on every mapping entry. Controls whether the mapping is applied.\n\n**Guidance**\nEmit `\"Active\"` for mappings that should run; `\"Draft\"` saves an\nin-progress mapping without the Active-only field validations. The\nAPI rejects a mapping missing this field\n(validation error: \"Mapping object must have status field present.\").\n","enum":["Active","Draft"]},"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**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":"Specifies the condition that triggers application of this mapping:\n- record_created: Apply only when creating a new record\n- record_updated: Apply only when updating an existing record\n- extract_not_empty: Apply only when the extract field returns a value\n","enum":["record_created","record_updated","extract_not_empty"]}}}},"required":["dataType"]},"MappingField":{"type":"object","description":"One Mapper 1.0 field-mapping entry.","properties":{"generate":{"type":"string","description":"Target field path to write on the destination record."},"extract":{"type":"string","description":"Source expression. Accepts:\n- JSONPath starting with `$.` (e.g. `$.customer.firstName`).\n- Handlebars template (contains `{{`) for concatenation /\n  formatting / conditional logic (e.g.\n  `{{record.firstName}} {{record.lastName}}`).\n- A literal string (any value not starting with `$.` and not\n  containing `{{`) — treated as a hardcoded value.\n\nOmit when using `hardCodedValue`.\n"},"hardCodedValue":{"type":["string","null"],"description":"Static value written to `generate` instead of extracting from the source record."},"dataType":{"type":"string","enum":["string","number","boolean","numberarray","stringarray","json"],"description":"Data type coercion applied to the mapped value."},"discardIfEmpty":{"type":"boolean","description":"When true, the field is omitted from the output when the extracted value is empty."},"immutable":{"type":"boolean","description":"When true, the mapped value cannot be overwritten by later mapping steps."},"lookupName":{"type":"string","description":"Name of an entry in the import's `lookups` array used to translate the value."},"default":{"type":["string","null"],"description":"Fallback value used when the extract yields no value."},"extractDateFormat":{"type":"string","description":"Date format of the source value, used to parse it before conversion."},"extractDateTimezone":{"type":"string","description":"Timezone applied when parsing the source date value."},"generateDateFormat":{"type":"string","description":"Date format applied to the value written to the destination."},"generateDateTimezone":{"type":"string","description":"Timezone applied when formatting the destination date value."},"conditional":{"type":"object","description":"Only apply this mapping entry when the given condition is satisfied.\n","properties":{"when":{"type":"string","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","expression"],"description":"Condition that gates whether this mapping entry is applied.\n`lookup_not_empty` / `lookup_empty` evaluate the lookup named by the\nsibling `lookupName`; `expression` evaluates the sibling `expression`.\n"},"lookupName":{"type":"string","description":"Lookup to evaluate for the `lookup_not_empty` / `lookup_empty`\nconditions.\n"},"expression":{"type":"string","description":"Expression evaluated when `when` is `expression`.\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.\nThis value typically matches the key in the fieldMap object.\n"},"name":{"type":"string","description":"Name of the field, used as the property name when generating the settings object\nfrom the submitted form data.\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.\n","default":false},"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 fields, specifies the placeholder and field name for the key input.\n"},"valueName":{"type":"string","description":"For keyvalue fields, specifies the placeholder and field name for the value input.\n"},"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.\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"]},"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.\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","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. This script must be accessible within the user's account\nand have appropriate permissions.\n"}}}}},"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"}}},"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},"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":"Reference to the integration app that defines this resource.\n\nThis field is only populated for resources that are part of an integration app.\nIt contains the unique identifier (_id) of the integration app (connector)\nthat defines the structure, behavior, and templates for this resource.\n\nThe integration app is the published template that can be installed\nmultiple times across different accounts, with each installation creating\na separate integration instance (referenced by _integrationId).\n\nThis reference enables:\n- Identifying the source integration app for this resource\n- Determining which template version is being used\n- Linking to documentation, support, and marketplace information\n"}}},"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/imports":{"get":{"summary":"List imports","description":"Returns a list of all imports configured in the account.\nIf no imports exist in the account, a 204 response with no body will be returned.\n","operationId":"listImports","tags":["Imports"],"parameters":[{"name":"externalId","in":"query","description":"Filter to imports matching this exact external identifier.","schema":{"type":"string"}},{"$ref":"#/components/parameters/Include"},{"$ref":"#/components/parameters/Exclude"}],"responses":{"200":{"description":"Successfully retrieved list of imports","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/Import"}}}}},"204":{"description":"No imports exist in the account"},"401":{"$ref":"#/components/responses/401-unauthorized"}}}}}}
````

## Create an import

> Creates a new import configuration that can be used to send data to applications\
> or external destinations.<br>

````json
{"openapi":"3.2.0","info":{"title":"Imports","version":"1.0.0"},"tags":[{"name":"Imports","description":"Imports deliver data to destination systems. An import receives pages of records from\nupstream flow steps, applies field mappings and transformations, and writes the results\nto the target application, database, or file destination — handling lookups, duplicate\navoidance, and per-record error reporting along the way. Each import uses one\n`adaptorType` that determines its connection compatibility and configuration object.\n\n## Import schema\n\n{% openapi-schemas spec=\"import\" schemas=\"Import\" 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 import. Set the adaptor-specific\nconfiguration object matching `adaptorType` (e.g. `netsuite_da` for `NetSuiteDistributedImport`).\n`_connectionId` is required except for the connection-less flavors (`ToolImport`,\n`AiAgentImport`, `GuardrailImport`): a `ToolImport` binds connections through the\nreferenced tool's `overrides`, and AI agent / guardrail imports only use a connection\nfor BYOK.","required":["name"],"allOf":[{"$ref":"#/components/schemas/ImportBase"}],"if":{"anyOf":[{"required":["adaptorType"],"properties":{"adaptorType":{"enum":["ToolImport","AiAgentImport","GuardrailImport"]}}},{"required":["tool"]},{"required":["aiAgent"]},{"required":["guardrail"]}]},"else":{"required":["_connectionId"],"if":{"properties":{"adaptorType":{"const":"NetSuiteHTTPImport"}},"required":["adaptorType"]},"then":{"required":["http","nsDomainType"]}}},"ImportBase":{"type":"object","description":"Writable import fields shared by the request and response schemas.","properties":{"_connectionId":{"type":"string","format":"objectId","description":"Connection this import uses to reach the destination system. The connection's type must\nbe compatible with the import's `adaptorType` (e.g. an `HTTPImport` needs an `http`\nconnection; `NetSuiteDistributedImport` and `NetSuiteHTTPImport` both need a `netsuite`\nconnection). Server-required — POST without it fails with 422 \"Expected field:\n_connectionId to be present\" — except for the connection-less flavors (`ToolImport`,\n`AiAgentImport`, `GuardrailImport`), which the server creates without one."},"_integrationId":{"type":["string","null"],"format":"objectId","description":"Integration this import belongs to."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this import was created from, set when the import is part of an installed integration app."},"adaptorType":{"type":"string","description":"Selects the adaptor technology that executes this import, which determines the compatible\nconnection types and which adaptor-specific configuration object must also be supplied\n(e.g. set `salesforce` when using `SalesforceImport`).","enum":["HTTPImport","FTPImport","AS2Import","S3Import","NetSuiteImport","NetSuiteDistributedImport","NetSuiteHTTPImport","SalesforceImport","JDBCImport","RDBMSImport","MongodbImport","DynamodbImport","WrapperImport","AiAgentImport","GuardrailImport","FileSystemImport","ToolImport","RESTImport"]},"nsDomainType":{"type":"string","enum":["suitetalk","restlet"],"description":"Selects which NetSuite REST host a `NetSuiteHTTPImport` 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 each\n`http.relativeURI` entry carries the complete path starting at `/services/rest/...` or\n`/app/site/hosting/restlet.nl`. Ignored on other adaptor types."},"externalId":{"type":["string","null"],"description":"External identifier for correlating the import with a record in another system."},"as2":{"$ref":"#/components/schemas/As2"},"dynamodb":{"$ref":"#/components/schemas/Dynamodb"},"http":{"$ref":"#/components/schemas/Http"},"ftp":{"$ref":"#/components/schemas/Ftp"},"jdbc":{"$ref":"#/components/schemas/Jdbc"},"mongodb":{"$ref":"#/components/schemas/Mongodb"},"netsuite":{"$ref":"#/components/schemas/NetSuite"},"netsuite_da":{"$ref":"#/components/schemas/NetsuiteDistributed"},"rdbms":{"$ref":"#/components/schemas/Rdbms"},"s3":{"$ref":"#/components/schemas/S3"},"wrapper":{"$ref":"#/components/schemas/Wrapper"},"salesforce":{"$ref":"#/components/schemas/Salesforce"},"tool":{"$ref":"#/components/schemas/Tool"},"file":{"$ref":"#/components/schemas/File"},"filesystem":{"$ref":"#/components/schemas/FileSystem"},"aiAgent":{"$ref":"#/components/schemas/AiAgentConfig"},"guardrail":{"$ref":"#/components/schemas/GuardrailConfig"},"name":{"type":"string","minLength":1,"maxLength":100,"description":"Display name for the import, shown in the flow builder, job history, and error logs.\nDescriptive, unique names indicating the destination system and purpose make large\naccounts easier to manage."},"description":{"type":["string","null"],"description":"Free-text summary of what the import writes and why. Shown in the UI and available to\nAI agents for context; has no effect on execution.","maxLength":5120},"unencrypted":{"type":"object","description":"Custom configuration values stored without encryption and returned in API responses."},"sampleData":{"type":["object","array","string"],"description":"Sample input record used to preview and build the import's mappings."},"distributed":{"type":"boolean","description":"When true, the import uses a distributed adaptor (such as `NetSuiteDistributedImport`)\nthat executes inside the target application rather than on Celigo's servers."},"maxAttempts":{"type":"number","description":"Maximum number of attempts made to deliver a record before it is marked as failed."},"ignoreExisting":{"type":"boolean","description":"When true, records that already exist in the destination system are silently skipped\ninstead of being created or updated — used for create-only operations that must avoid\nduplicates. Existing records are identified by the import's lookup configuration or by\na populated `ignoreExtract` field on the incoming record."},"ignoreMissing":{"type":"boolean","description":"When true, records that do not already exist in the destination system are silently\nskipped instead of producing errors — used for update-only operations."},"idLockTemplate":{"type":["string","null"],"description":"Handlebars template that generates a lock key for each record so records resolving to\nthe same key are not submitted concurrently, preventing duplicate or conflicting writes\nto the same target record."},"dataURITemplate":{"type":["string","null"],"description":"Handlebars template that builds a link back to each record in the destination\napplication's UI. The resolved URL is stored with error records in job history so users\ncan jump straight to the record."},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"blobKeyPath":{"type":"string","description":"Path in the input record that holds the blob key identifying the file content to import.\nAt send time the platform follows this path, retrieves the referenced file from integrator.io\nstorage, and streams its bytes into the outgoing request."},"blob":{"type":"boolean","description":"When true, this import transfers raw file content (blobs) to the destination rather than structured records."},"assistant":{"type":"string","description":"Identifier for the connector assistant used to configure this import."},"deleteAfterImport":{"type":"boolean","description":"When true, the source file is deleted after it is successfully imported."},"assistantMetadata":{"type":"object","description":"Metadata associated with the connector assistant configuration."},"useTechAdaptorForm":{"type":"boolean","description":"When true, the UI presents the full technical adaptor form for this import instead of\nthe simplified assistant form."},"distributedAdaptorData":{"type":"object","description":"Internal state stored by distributed adaptors (such as the NetSuite SuiteApp) for this import."},"filter":{"description":"Filter applied to incoming records before they are sent to the destination system.\nRecords that match continue through the import; records that don't are silently\ndropped. Filter expressions reference the incoming record's fields.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"traceKeyTemplate":{"type":"string","description":"Handlebars template that overrides how each record's unique trace key is generated,\nused to track records through the flow and match errors to records. Trace keys are\ncapped at 256 characters; a longer key is stored truncated from the middle, keeping\nthe beginning and end of the value."},"mockResponse":{"type":"array","description":"Predefined response records used in place of calling the destination system when\ntesting the import, so flows can run without writing real data. Records must be in\nintegrator.io canonical format — the server rejects any other shape with a 422.","items":{"type":"object","properties":{"statusCode":{"type":"integer","description":"HTTP status code the mock returns (e.g. 200, 403)."},"id":{"type":["string","integer"],"description":"Identifier echoed for the mocked record."},"ignored":{"type":"boolean","description":"When true, the mocked record reports as ignored rather than imported."},"dataURI":{"type":"string","description":"Data URI echoed for the mocked record."},"errors":{"type":"array","description":"Mock error entries returned with the record."},"_json":{"type":["object","array"],"description":"Mock response body."},"_headers":{"type":"object","description":"Mock response headers."}}}},"_ediProfileId":{"type":"string","format":"objectId","description":"EDI profile this import uses to generate outbound X12 or EDIFACT documents — it supplies\nthe envelope qualifiers, delimiters, version, and validation rules. Set it when the\nimport produces EDI output; omit it otherwise. Accepted on the file-writing imports —\nFTP, AS2, S3 and HTTP file mode (`http.type: \"file\"`)."},"parsers":{"type":"array","description":"Legacy parser configuration slot. The server initializes this field to an empty array\nand current API writes never populate it; treat it as server bookkeeping rather than a\nsetting to configure."},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the import lifecycle for\ntransformations, validation, and custom result handling beyond what configuration alone\ncan express.","properties":{"preMap":{"type":"object","description":"Hook that runs on each page of records before the import's mappings are applied.\nCommonly used to reshape or filter records ahead of mapping.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postMap":{"type":"object","description":"Hook that runs after the import's mappings are applied but before records are sent\nto the destination system. Commonly used for adjustments that need the mapped\n(destination-shaped) record.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postSubmit":{"type":"object","description":"Hook that runs after the destination system responds, with access to both the\nsubmitted records and the destination's responses. Commonly used to inspect results\nor adjust the response data passed downstream.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postAggregate":{"type":"object","description":"Hook that runs after an aggregated file has been submitted to the destination, for\nfile-based imports that combine records into a single file.","allOf":[{"$ref":"#/components/schemas/Hook"}]}}},"sampleResponseData":{"type":["object","array","string"],"description":"Sample response payload used to preview response mappings and test downstream steps\nwithout calling the destination system."},"responseTransform":{"description":"Transformation that reshapes the destination system's response after records are\nimported, before the response is processed by response mappings and downstream\nsteps. Commonly used to extract relevant fields from verbose API responses.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"modelMetadata":{"type":"object","description":"Metadata about the destination data model captured for this import. Rarely set."},"mapping":{"type":"object","description":"Mapper 1.0 mapping configuration. `fields` maps individual fields; `lists` maps\nrecord lists, each with its own `fields` array. Superseded by Mapper 2.0\n(`mappings`) but still widely used.","properties":{"fields":{"type":"array","description":"Field-level mapping entries applied to each record.","items":{"$ref":"#/components/schemas/MappingField"}},"lists":{"type":"array","description":"List-level mappings, each generating a sublist on the target record.","items":{"type":"object","properties":{"generate":{"type":"string","description":"Target sublist or array path to generate."},"fields":{"type":"array","description":"Field mappings applied within each generated list item.","items":{"$ref":"#/components/schemas/MappingField"}}}}}}},"mappings":{"description":"Field mappings that transform incoming records into the destination system's field\nstructure — renaming fields, converting types, building nested objects, and applying\nformulas or lookups to derive values.","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"lookups":{"description":"Top-level mirror of the adaptor-specific ``lookups`` array. Celigo persists lookups\nin TWO locations on every import resource: ``<adaptor_key>.lookups`` (the typed,\nadaptor-specific definition) and this top-level ``lookups`` array (a permissive copy).","allOf":[{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the shape of the input passed to the import's processing pipeline."},"settingsForm":{"$ref":"#/components/schemas/Form"},"preSave":{"$ref":"#/components/schemas/PreSave"},"settings":{"$ref":"#/components/schemas/Settings"}}},"As2":{"type":"object","description":"Configures how outbound AS2 messages are built for this import. Required when the\n_connectionId field references an AS2 connection. AS2 (Applicability Statement 2) transmits\nEDI and other data securely over HTTP/S using S/MIME encryption and digital signatures; this\nobject names the payload file, the message identifier, retry behavior, and any extra headers.","required":["fileNameTemplate"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies the partner-specific EDI and AS2 configuration.\nWhen set, the import inherits the connector's pre-configured settings; omit to use only\nthe AS2 connection details."},"fileNameTemplate":{"type":"string","description":"Handlebars template that names the file carrying each AS2 message payload. Include a\nuniqueness token such as {{timestamp}} so concurrent or repeated sends do not collide;\ndefaults to file-{{timestamp}} in the form."},"messageIdTemplate":{"type":"string","description":"Handlebars template that generates the Message-ID header for each AS2 message. Placeholders\nare replaced at runtime; the resulting value must be globally unique and follow RFC 5322\nheader formatting. Leave unset to let the platform generate the Message-ID."},"maxRetries":{"type":"number","default":0,"description":"Number of times a failed transmission is retried after transient errors such as network\nfailures or timeouts; does not affect the initial send. The form offers 1–5; set to 0\n(the default) to report failure immediately without retrying."},"headers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header sent with the AS2 message (for example, `content-disposition`). Header names are case-insensitive."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions rendered against the record."}}},"description":"HTTP headers sent with the AS2 message transmission.\nSupports both standard AS2 headers and custom headers required by trading partner agreements."}}},"Dynamodb":{"type":"object","description":"Configuration for DynamoDB imports. Required when the _connectionId field references a\nDynamoDB connection; must not be included for other connection types. putItem writes a full\nitem document; updateItem modifies named attributes of an existing item identified by its\nkey.","required":["region","method","tableName"],"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"],"default":"us-east-1","description":"AWS region hosting the DynamoDB table. Determines the service endpoint used for all\nrequests and must match the region where the table is deployed."},"method":{"type":"string","enum":["putItem","updateItem"],"description":"Write operation performed against the DynamoDB table for each record."},"tableName":{"type":"string","description":"DynamoDB table that receives the imported records.\nMust match an existing table in the target AWS account and region; table names are case-sensitive."},"partitionKey":{"type":"string","description":"Value of the target item's partition key for the updateItem operation; together with\nsortKey (when the table defines one) it identifies the item each record writes to.\nSupports handlebars to reference the incoming record's fields."},"sortKey":{"type":"string","description":"Value of the target item's sort key; supports handlebars. Omit when the table defines only\na partition key."},"itemDocument":{"type":"string","description":"JSON document written to the table for the putItem operation, containing all attribute\nnames and values for the item, including its key attributes. Supports handlebars."},"updateExpression":{"type":"string","description":"DynamoDB update expression naming which attributes to set, add, or remove on the item for\nthe updateItem operation. Only the named attributes change; reference attribute names and\nvalues through the expressionAttributeNames and expressionAttributeValues placeholders."},"conditionExpression":{"type":"string","description":"Condition evaluated against the existing item before the write executes.\nWhen the condition evaluates to false, the operation is aborted and no changes are made."},"expressionAttributeNames":{"type":"string","description":"JSON placeholder-to-name map for attribute names referenced in expressions, for example {\"#N\": \"Name\"}.\nUse it when an attribute name is a reserved word or contains special characters; placeholder keys start with #."},"expressionAttributeValues":{"type":"string","description":"JSON placeholder-to-value map for attribute values referenced in expressions; placeholder\nkeys start with a colon (e.g. \":val\") and each value is a DynamoDB attribute-value object\nsuch as {\"S\": \"text\"}. Supports handlebars."},"ignoreExtract":{"type":"string","description":"Path to the field in the source record used to determine whether the record already exists.\nOnly used when ignoreExisting is set on the import."}},"if":{"properties":{"method":{"const":"updateItem"}},"required":["method"]},"then":{"required":["partitionKey","updateExpression"]}},"Http":{"type":"object","description":"Configuration for HTTP imports.\nRequired whenever the connection referenced by _connectionId has type http, and on\n`NetSuiteHTTPImport` (NetSuite's REST APIs on a `netsuite` connection, where `nsDomainType`\nsupplies the host).","properties":{"sendPostMappedData":{"type":"boolean","default":true,"description":"When true, the request body is the record as reshaped by the import's mapping step. When false, the original pre-mapped record is sent instead, bypassing the mappings for the payload."},"isRest":{"type":"boolean","description":"Internal flag indicating the import was built with the legacy REST form rather than the unified HTTP framework. Set by the form; not configured directly."},"strictHandlebarEvaluation":{"type":"boolean","description":"When true, handlebars expressions that reference a missing field raise an error instead of rendering an empty string, surfacing mapping mistakes early rather than sending blank values."},"formType":{"type":"string","enum":["assistant","http","rest","graph_ql","assistant_graphql"],"description":"Determines the UI form and configuration experience for this import."},"type":{"type":"string","enum":["file","records"],"description":"Controls whether the import sends structured record data or raw file content.\nMost HTTP imports use records; use file only when importing raw file content that will be processed downstream."},"requestMediaType":{"type":"string","enum":["xml","json","csv","urlencoded","form-data","octet-stream","plaintext"],"description":"Content type used to serialize the request body sent to the target API.\nMost REST APIs use json."},"_httpConnectorEndpointIds":{"type":"array","readOnly":true,"items":{"type":"string","format":"objectId"},"description":"HTTP connector endpoint IDs used by this import (multiple endpoints for different request\ntypes or operations). Set by the connector framework; client-supplied values are ignored."},"blobFormat":{"type":"string","enum":["utf8","ucs2","utf-16le","ascii","binary","base64","hex"],"description":"Character encoding format for blob/binary data imports.\nOnly relevant when type is \"file\" or when handling binary content.\n"},"batchSize":{"type":"integer","description":"Maximum number of records submitted per HTTP request, which affects throughput and API rate limiting.\nREST services typically send one record per request; raise it only for batch endpoints or RPC/XML services that accept multiple records, consulting the target API documentation for the optimal value.\n\nThe default varies by API."},"successMediaType":{"type":"string","enum":["xml","json","plaintext"],"description":"Media type expected in successful responses from the target API. Most APIs return json."},"requestType":{"type":"array","items":{"type":"string","enum":["CREATE","UPDATE"]},"description":"Operation type each request performs; for composite (upsert) imports, include both values.\nThe array is positionally aligned with the relativeURI and method arrays — each index maps to one operation — and the existingExtract field determines which operation runs at runtime."},"errorMediaType":{"type":"string","enum":["xml","json","plaintext"],"description":"Media type expected in error responses from the target API. Most APIs return json."},"followRedirects":{"type":"boolean","default":true,"description":"When explicitly false, 3xx responses are not followed — the redirect\nresponse itself (status code, `Location` header, body) is what the\nimport records as the result. Omitted or true follows allowed\nredirects (the default behavior)."},"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":"array","items":{"type":"string"},"description":"Relative URI path for each import request, as an array of strings (never an object); values can include Handlebars expressions for dynamic segments.\nTemplates render against the pre-mapped record — the original input before the Import's mapping step — because URI construction usually needs business identifiers that mappings may rename or remove.\nFor composite (upsert) imports, the array is positionally aligned with method and requestType, and existingExtract decides the index used at runtime: the UPDATE index when its field has a value, the CREATE index when it is empty or missing.\nReference the existing record ID in the UPDATE URI with `{{{data.0.fieldName}}}` (triple braces with the data.0 prefix).\nUse separate array elements per operation rather than `{{#if}}` conditionals inside a single URI."},"method":{"type":"array","items":{"type":"string","enum":["GET","PUT","POST","PATCH","DELETE"]},"description":"HTTP method used for each import request.\nFor composite (upsert) imports, the array is positionally aligned with relativeURI and requestType — e.g. `[\"PUT\", \"POST\"]` with `requestType: [\"UPDATE\", \"CREATE\"]` uses PUT for updates and POST for creates."},"_httpConnectorVersionId":{"type":"string","format":"objectId","readOnly":true,"description":"HTTP connector version used by this import. Set by the connector framework; client-supplied values are ignored (write-tested)."},"_httpConnectorResourceId":{"type":"string","format":"objectId","readOnly":true,"description":"HTTP connector resource used by this import. Set by the connector framework; client-supplied values are ignored (write-tested)."},"_httpConnectorEndpointId":{"type":"string","format":"objectId","readOnly":true,"description":"HTTP connector endpoint used by this import (single endpoint). Set by the connector framework; client-supplied values are ignored (write-tested)."},"body":{"type":"array","items":{"type":["string","null"]},"description":"Request body template for the import — an array of Handlebars template strings\npositionally aligned with the `method` array (e.g. `[\"{{{record}}}\"]`). A `null`\nentry means no template for the method at that position. Each entry is a string:\nwrite a JSON body as a template string (`[\"{\\\"id\\\": \\\"{{record.id}}\\\"}\"]`), not as\na JSON object, because an object entry does not render as JSON at runtime.\nLeave undefined for standard imports — the Import's mapping step transforms the source record and the mapped data is sent as the request body automatically.\nSet it only when the target API requires XML, SOAP, or a custom structure that mappings cannot produce, or when the user explicitly specifies a request body.\nWhen set, the mapping step still runs first and the template renders against the post-mapped record — use `{{record.<mappedField>}}` to reference mapping outputs.\nHand-templated bodies are harder to maintain and debug than mappings; when in doubt, leave this field undefined.\n\nCustom envelopes can combine static header or account fields with per-message\nfield references inside a loop.\n\nStatic wrappers around the mapped record are appropriate for message-bus class envelopes,\nHMAC signature wrappers, integration batch headers,\nnamed single-record array wrappers, and similar destination-required structures.\n\nBatch templates can wrap an envelope, such as a header, version, or marketplace,\naround serialized records.\n\nUse this pattern when each message needs a small number of fields rather than the whole record."},"ignoreEmptyNodes":{"type":"boolean","description":"When true, fields with empty values are stripped from the request body before it is sent,\nso the target API receives only populated fields. Shown as \"Remove empty fields from HTTP\nrequest body\" in the UI."},"existingExtract":{"type":"string","description":"Field name or JSON path that drives the upsert decision for composite imports with both CREATE and UPDATE in requestType.\nWhen the field has a value in the incoming record, the UPDATE operation's relativeURI and method are used; when it is empty or missing, the CREATE operation runs.\nMust match a field populated by an upstream lookup or response mapping (e.g. a destination system ID like \"shopifyCustomerId\"); omit for single-operation imports."},"existingLookupName":{"type":"string","description":"Name of an entry in `lookups` that resolves whether each record already exists in the destination, driving the composite upsert decision when `existingExtract` alone is insufficient (e.g. the check requires a call to the target system). Paired with composite CREATE/UPDATE imports.\n\nUse `existingLookupName` when the incoming record has no destination ID and\nneeds a per-record probe to discover one;\nuse `existingExtract` when the record already carries the ID.\n\nFor single-operation imports,\nuse `ignoreExtract` or `ignoreLookupName` with `ignoreExisting` or\n`ignoreMissing` to skip records instead of routing them.\n\nThe lookup pattern adds one HTTP request per record at runtime,\nmaking it better suited to one-off migrations and low-volume reconciliation than\nhigh-volume recurring syncs."},"ignoreExtract":{"type":"string","description":"Field name on the incoming record that drives the SKIP-vs-PROCESS\ndecision when ``ignoreExisting`` or ``ignoreMissing`` is set.\n\nPairs with either of the two skip-mode flags:\n\n- With ``ignoreExisting: true`` (CREATE-only imports that should\n  avoid duplicates): if ``ignoreExtract`` has a value on the\n  record, the record is treated as already-existing and is\n  skipped; if empty, the record is created.\n- With ``ignoreMissing: true`` (UPDATE-only imports that should\n  avoid creating new records): if ``ignoreExtract`` has a value,\n  the record is treated as existing and is updated; if empty,\n  the record is skipped.\n\n``ignoreExtract`` checks a field that's already on the record —\nzero per-record HTTP overhead.  Use ``ignoreLookupName`` instead\nwhen the record has no field to inspect and the existence check\nneeds a per-record probe against the destination.\n\n**Mutually exclusive with ``ignoreLookupName``**\n\nSet EXACTLY ONE of ``ignoreExtract`` / ``ignoreLookupName`` when\n``ignoreExisting`` or ``ignoreMissing`` is enabled.\n\n**When not to set this field**\n\nOnly valid for single-operation imports with either\n``ignoreExisting: true`` or ``ignoreMissing: true`` set.\nComposite (upsert) imports — those with both ``CREATE`` and\n``UPDATE`` in ``requestType`` — use ``existingExtract`` /\n``existingLookupName`` instead, because they route records\nbetween two write paths rather than skipping them."},"endPointBodyLimit":{"type":"integer","description":"Maximum size limit for the request body in bytes.\nUsed to enforce API-specific size constraints.\n"},"headers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header to include with each import request."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions rendered against the pre-mapped record."}}},"description":"Custom HTTP headers included with import requests; values can contain Handlebars expressions.\nValue templates render against the pre-mapped record (the original input before the Import's mapping step) — use `{{record.<field>}}` to reference fields as they appear in the upstream source."},"response":{"type":"object","properties":{"resourcePath":{"type":"array","items":{"type":"string"},"description":"Per-operation JSON path to the resource collection in each\nresponse.  Outer-array length MUST match ``requestType``\nlength: 1 entry for single-op imports, N entries for an\nN-operation composite import (duplicate the value when\noperations agree).  See the ``response`` object docs\nabove for the full positional-array contract.\n\nRequired when ``batchSize > 1`` because the platform needs\nto know where the array of result records lives inside the\nresponse envelope.  When the API returns a bare array at\nthe top level, leave this field unset.\n\nEach entry is a DOT-PATH STRING (e.g. ``\"data.results\"``,\n``\"items\"``) — do NOT split paths into segments across\narray elements."},"resourceIdPath":{"type":"array","items":{"type":["string","null"]},"description":"Per-operation JSON path to the unique ID field within each\nrecord in the response.  Outer-array length MUST match\n``requestType`` length — duplicate the value when both\noperations agree (the common case) rather than collapsing\nto a 1-entry array.\n\nEach entry is a DOT-PATH STRING, or null for an operation\nwith no ID path configured. When not specified, the platform\nlooks for standard `id` or `_id` fields automatically."},"successPath":{"type":"array","items":{"type":"string"},"description":"Per-operation JSON path to a field that indicates whether\nthe API call succeeded.  Outer-array length MUST match\n``requestType`` length — duplicate the value when both\noperations agree rather than collapsing to a 1-entry\narray on a composite import.\n\nUse this when the API returns HTTP 200 for everything and\nsignals success / failure through a body field.  Pairs with\n``successValues`` to define which values at this path mean\nsuccess.\n\nEach entry is a DOT-PATH STRING."},"successValues":{"type":"array","items":{"type":"array","items":{"type":"string"}},"description":"Per-operation list of values at ``successPath`` that mean\nsuccess.  TWO levels of array nesting:\n\n- Outer array: one entry per ``requestType`` index.\n  Length MUST match ``requestType`` length — duplicate\n  the inner array when both operations agree rather than\n  collapsing to a 1-entry outer array on a composite\n  import.\n- Inner array: the list of acceptable success values for\n  that operation.\n\nAll comparisons are STRING comparisons regardless of the\nactual response value type — the platform stringifies\nnumbers, booleans, etc. before comparing."},"failPath":{"type":"array","items":{"type":"string"},"description":"Per-operation JSON path to a field that signals failure\neven when HTTP returns 200.  Outer-array length MUST\nmatch ``requestType`` length — duplicate when both\noperations agree.  Same array semantics as ``successPath``.\n\nSome APIs use only ``successPath`` (failure = absence of\nsuccess); others publish both an explicit success indicator\nand an explicit failure indicator.  When both are\navailable, set both — the platform evaluates ``successPath``\nfirst and falls through to ``failPath`` only when the\nsuccess check is inconclusive."},"failValues":{"type":"array","items":{"type":"array","items":{"type":"string"}},"description":"Per-operation list of values at ``failPath`` that mean\nfailure.  Same two-level array shape and length contract\nas ``successValues`` — outer length MUST match\n``requestType`` length, duplicate the inner array on\ncomposite imports when both operations agree.\n\nOften used as the inverse of ``successValues`` (e.g.\n``successValues: [[\"true\"]]`` paired with\n``failValues: [[\"false\"]]``)."},"errorPath":{"type":"string","description":"JSON path to the error message inside the response body.\n\n**Note**: this field is NOT a per-operation array — it's a\nsingle string applied to every operation.  Most APIs use\nthe same error-message field shape for both UPDATE and\nCREATE responses, so the platform doesn't expose a per-op\noverride here."},"allowArrayforSuccessPath":{"type":"boolean","description":"When true, allows array values at successPath during success evaluation."},"hasHeader":{"type":"boolean","description":"When true, treats the first record in the response as a header row (for CSV responses)."}},"description":"Configuration for parsing and interpreting HTTP responses returned\nfrom each write request the import issues.\n\n**Critical: positional per-operation arrays**\n\nEvery field inside ``response`` (``resourcePath``, ``resourceIdPath``,\n``successPath``, ``successValues``, ``failPath``, ``failValues``)\nis an ARRAY whose outer length is positionally aligned with\n``requestType`` / ``method`` / ``relativeURI``.  This is the\nSAME positional-alignment contract as those three sibling\narrays — see ``relativeURI`` docs for the full explanation.\n\nEach outer-array index describes how to parse the response from\nthe operation at that same index in ``requestType``:\n\n- **Single-operation import** (``requestType: [\"CREATE\"]``):\n  every response field is a 1-element array.\n  ``successPath: [\"ok\"]`` means \"for the one operation,\n  look at path ``ok`` to detect success\".\n- **Composite upsert** (``requestType: [\"UPDATE\", \"CREATE\"]``):\n  every response field is a 2-element array.  Index 0 parses\n  the UPDATE response, index 1 parses the CREATE response.\n\n**successValues / failValues — array of arrays**\n\n``successValues`` and ``failValues`` carry an extra array level\nbecause each operation can have MULTIPLE acceptable values:\n\n- Outer array: one entry per operation (positional, as above).\n- Inner array: the list of acceptable values for that operation.\n\nExample:\n```json\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"successPath\": [\"ok\", \"ok\"],\n\"successValues\": [[\"true\", \"1\"], [\"true\"]]\n```\nMeans: the UPDATE response is successful when ``ok`` is either\n``\"true\"`` or ``\"1\"``; the CREATE response is successful only\nwhen ``ok`` is ``\"true\"``.\n\n**Always duplicate when operations agree (REQUIRED for composite)**\n\nThe outer-array length MUST match ``requestType`` length on\nevery response field.  When the UPDATE and CREATE responses\nof the same connector look the same (which is the common\ncase — most APIs return the same shape from both endpoints),\nDUPLICATE the entry so the array has the same length as\n``requestType``.  Do NOT collapse to a 1-entry array on a\ncomposite import.\n\n```json\n// ✅ Correct — explicit per-operation entries, even when identical\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data.id\", \"data.id\"],\n\"successPath\": [\"success\", \"success\"],\n\"successValues\": [[\"true\"], [\"true\"]]\n```\n\n```json\n// ❌ Wrong — 1-entry array on a composite import\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data.id\"],\n\"successPath\": [\"success\"],\n\"successValues\": [[\"true\"]]\n```\n\nWhy duplicate even when they agree:\n\n- **UI parity.**  The Celigo UI renders one response-handling\n  form per ``requestType`` index.  A 1-entry array on a\n  composite import leaves the second operation's form panel\n  empty, which looks like a half-built config to the user\n  reviewing the saved import — even if it works at runtime.\n- **Symmetric shape rule.**  ``relativeURI`` / ``method`` /\n  ``requestType`` REQUIRE the entry count to match across\n  all three (the runtime decider uses positional alignment).\n  Making ``response.*`` follow the same \"outer length ==\n  requestType length\" rule means there's ONE shape rule to\n  remember, not two.\n- **Unambiguous intent.**  A 2-entry duplicated array\n  documents in the saved config that the developer\n  considered both operations.  A 1-entry array could mean\n  \"both ops use this\" or could mean \"I forgot to fill in\n  the second op.\"  Duplication eliminates the ambiguity.\n\nWhen operations DIFFER (rare — different response shapes\nper endpoint), set distinct values at each index.  Either\nway the outer length always matches ``requestType``.\n\n**Common llm mistake (DO not do THIS)**\n\nDo NOT split a single dot-path into array segments thinking the\nouter array represents path segments:\n\n```json\n// WRONG — the LLM expressed \"data.id\" as path segments\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data\", \"id\"]\n```\n\nThis is interpreted by the platform as: UPDATE response's id is\nat path ``data``, CREATE response's id is at path ``id`` — almost\nnever what the user wants.  The CORRECT shape is the single\ndot-path expressed as ONE string per operation, duplicated to\nmatch ``requestType`` length:\n\n```json\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data.id\", \"data.id\"]\n```"},"_asyncHelperId":{"type":"string","format":"objectId","description":"Reference to an AsyncHelper resource that polls an asynchronous\ndestination API on this import's behalf.\n\nSet this ONLY when the destination API is genuinely asynchronous — it\nacknowledges the submitted payload (HTTP 202, a job ticket) and ingests\nit in the background, so completion must be polled for (e.g. bulk\nimage/file ingestion, large bulk-load endpoints). Most imports are\nsynchronous and need NO async helper; adding one to a synchronous\ndestination just adds polling overhead plus a status and result export\nto maintain. When in doubt, leave it unset.\n\nThe referenced helper bundles the polling config plus a required status\nexport (polled to check progress) and a result export (fetches the\nfinal payload). An import configured with an async helper cannot carry\nits own transform, output filter, or preSavePage hook — put that\nprocessing in the result export instead."},"ignoreLookupName":{"type":"string","description":"Name of an entry in `lookups` used to check whether each record already exists, so `ignoreMissing`/`ignoreExisting` can skip it. Use it instead of `ignoreExtract` when existence must be resolved by a call to the target system rather than read from a field on the incoming record."},"lookups":{"description":"Lookups referenced by `existingLookupName`/`ignoreLookupName` to resolve record existence against the destination system at runtime.","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia ``lookupName`` and Handlebars expressions reference it via\n``{{lookup.name}}``. Must be unique within this\nresource's ``lookups`` array.\n"},"method":{"type":"string","enum":["GET","POST","PUT","PATCH","DELETE"],"description":"HTTP method used to issue the lookup request. ``GET`` and\n``POST`` are most common. Omit when using a static ``map`` or\n``_lookupCacheId``.\n"},"relativeURI":{"type":"string","description":"HTTP path (relative to the connection's base URL) that\nresolves the lookup. Use Handlebars with triple braces to\ninject values from the incoming record — e.g.\n``/customers?email={{{email}}}`` or\n``/accounts/{{{accountId}}}``.\n"},"postBody":{"type":"string","description":"Request body template for POST / PUT / PATCH lookup methods.\nTypically a JSON string with Handlebars placeholders —\n``{\"email\":\"{{{email}}}\"}``. Ignored for GET / DELETE.\n"},"headers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}},"description":"Optional custom headers to attach to the lookup request.\nUseful for endpoints that need a different Accept header or\nan auxiliary auth token separate from the connection-level\nheaders.\n"},"extract":{"type":"string","description":"JSONPath expression that selects the lookup value from the\nHTTP response body. ``$.`` prefix optional. Examples:\n``$.data[0].id``, ``$.results.primaryKey``, ``id``.\n"},"_lookupCacheId":{"type":"string","format":"objectId","description":"Optional reference to a LookupCache resource. When set, the\nlookup reads from the cache instead of issuing a live HTTP\nrequest — useful for stable reference data that should be\npre-loaded.\n"},"map":{"type":["object","null"],"description":"Optional static key→value object evaluated BEFORE the HTTP\nrequest. When the incoming value matches a key, the mapped\nvalue is returned without hitting the remote endpoint. Null\non dynamic lookups with no static map.\n"},"default":{"type":["string","null"],"description":"Value returned when the HTTP request returns no usable\nmatch. When omitted and ``allowFailures`` is false, an\nunmatched lookup halts the record. Stored as null when no\nfallback is configured.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, a lookup miss (no matching response and no\n``default``) resolves to ``null`` and the import continues.\nWhen false (default), a miss halts the record. May be\nstored as null (treated as unset).\n"},"useDefaultOnMultipleMatches":{"type":"boolean","description":"Controls behaviour when the response contains more than one\nmatch. When true, the ``default`` value is returned instead\nof raising an ambiguous-match error. When false (default),\nmultiple matches are treated as a failure.\n"}}},"type":"array"}}},"Ftp":{"type":"object","description":"Defines where files are written on 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 target folder and the file naming comes from\nthe import's file configuration.","required":["directoryPath"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies partner-specific B2B settings for this import.\nWhen set, the import inherits the connector's pre-configured settings; omit to use only\nthe FTP connection details."},"directoryPath":{"type":"string","description":"Directory on the server where imported files are written, either absolute or relative to\nthe login directory; the FTP user must have write permission on it. Use forward slashes\nregardless of server OS — paths are case-sensitive on UNIX/Linux servers. Supports\nhandlebars templates."},"fileName":{"type":"string","description":"Name of the file written to the server, including its extension; do not include directory\nseparators — the location comes from directoryPath. Supports handlebars placeholders such\nas items-{{timestamp}}.csv to generate a unique name per run."},"inProgressFileName":{"type":"string","description":"Temporary name the file carries while its upload is in progress, preventing other systems\nfrom processing a partially transferred file; it is renamed to its final name once the\nupload completes. Include a handlebars uniqueness token (e.g. {{timestamp}}) when one file\nis written per flow run."},"backupDirectoryPath":{"type":"string","description":"Directory on the same server where a copy of each written file is retained after a\nsuccessful import; if omitted, no server-side backup is kept. Supports static paths or\nhandlebars templates."}}},"Jdbc":{"type":"object","description":"Configuration for JDBC import operations. Defines how data is written to a database\nvia a JDBC connection.\n\n**Query type determines which fields are required**\n\n| queryType        | Required fields              | Do NOT set        |\n|------------------|------------------------------|-------------------|\n| [\"per_record\"]   | query (array of SQL strings) | bulkInsert        |\n| [\"per_page\"]     | query (array of SQL strings) | bulkInsert        |\n| [\"bulk_insert\"]  | bulkInsert object            | query             |\n| [\"bulk_load\"]    | bulkLoad object              | query             |\n\n**Critical:** query IS AN ARRAY OF STRINGS\nThe query field must be an array of plain strings, NOT a single string and NOT an array of objects.\nCorrect: [\"INSERT INTO users (name) VALUES ('{{{name}}}')\"]\nWrong: \"INSERT INTO users ...\"\nWrong: [{\"query\": \"INSERT INTO users ...\"}]","required":["queryType"],"properties":{"query":{"type":"array","items":{"type":"string"},"description":"Array of SQL query strings to execute. REQUIRED when queryType is [\"per_record\"] or [\"per_page\"].\n\nEach element is a complete SQL statement as a plain string. Typically contains a single query.\n\nUse Handlebars {{fieldName}} syntax to inject values from incoming records.\n\n**Format — array of strings**\n- CORRECT: [\"INSERT INTO users (name, email) VALUES ('{{{name}}}', '{{{email}}}')\"]\n- WRONG: \"INSERT INTO users ...\"  (not an array)\n- WRONG: [{\"query\": \"INSERT INTO users ...\"}]  (objects are invalid — causes Cast error)\n\n**Examples**\n- INSERT: [\"INSERT INTO users (name, email) VALUES ('{{{name}}}', '{{{email}}}')\"]\n- UPDATE: [\"UPDATE inventory SET qty = {{{quantity}}} WHERE sku = '{{{sku}}}'\"]\n- MERGE/UPSERT: [\"MERGE INTO target USING (SELECT CAST(? AS VARCHAR) AS email) AS src ON target.email = src.email WHEN MATCHED THEN UPDATE SET name = ? WHEN NOT MATCHED THEN INSERT (name, email) VALUES (?, ?)\"]\n\n**String vs numeric values in handlebars**\n- Use triple braces so the raw value is emitted; double braces emit the value already single-quoted, so wrapping them in quotes double-quotes it.\n- Strings: supply the quotes yourself — '{{{name}}}'\n- Numbers: no quotes — {{{quantity}}}"},"queryType":{"type":"array","items":{"type":"string","enum":["bulk_insert","per_record","per_page","bulk_load"]},"description":"Execution strategy for the SQL operation. REQUIRED. Must be an array with one value.\n\n**Decision tree**\n\n1. If UPDATE or UPSERT/MERGE → [\"per_record\"] (set query field)\n2. If INSERT with \"ignore existing\" / \"skip duplicates\" / match logic → [\"per_record\"] (set query field)\n3. If pure INSERT with no duplicate checking → [\"bulk_insert\"] (set bulkInsert object)\n4. If high-volume bulk load → [\"bulk_load\"] (set bulkLoad object)\n\n**Critical relationship to other fields**\n| queryType        | REQUIRES              | DO NOT SET   |\n|------------------|-----------------------|--------------|\n| [\"per_record\"]   | query (array)         | bulkInsert   |\n| [\"per_page\"]     | query (array)         | bulkInsert   |\n| [\"bulk_insert\"]  | bulkInsert object     | query        |\n| [\"bulk_load\"]    | bulkLoad object       | query        |\n\n**Examples**\n- Per-record upsert: [\"per_record\"]\n- Bulk insert: [\"bulk_insert\"]\n- Bulk load: [\"bulk_load\"]\n"},"bulkInsert":{"type":"object","description":"Bulk insert configuration. REQUIRED when queryType is [\"bulk_insert\"]. DO NOT SET when queryType is [\"per_record\"].\n\nEnables efficient batch insertion of records into a database table without per-record SQL.\n","properties":{"tableName":{"type":"string","description":"Target database table name for bulk insert. REQUIRED.\nCan include schema qualifiers (e.g., \"schema.tableName\").\n"},"batchSize":{"type":"string","description":"Number of records per batch during bulk insert.\nLarger values improve throughput but use more memory.\nCommon values: \"1000\", \"5000\".\n"}}},"bulkLoad":{"type":"object","description":"Bulk load configuration. REQUIRED when queryType is [\"bulk_load\"]. Uses database-native bulk loading for maximum throughput.\n","properties":{"tableName":{"type":"string","description":"Target database table name for bulk load. REQUIRED.\nCan include schema qualifiers (e.g., \"schema.tableName\").\n"},"primaryKeys":{"type":["array","null"],"items":{"type":"string"},"description":"Primary key column names for upsert/merge during bulk load.\nWhen set, existing rows matching these keys are updated; non-matching rows are inserted.\nExample: [\"id\"] or [\"order_id\", \"product_id\"] for composite keys.\n"},"overrideMergeOrInsertQuery":{"type":"boolean","description":"When true, a custom merge/insert query replaces the auto-generated statement for the\nbulk load, enabling ignore-existing logic or conditional updates."}}},"lookups":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia `lookupName` and Handlebars expressions reference it via\n`{{lookup \"name\" value}}`. Must be unique within this\nresource's `lookups` array."},"query":{"type":"string","description":"SQL query for the lookup (e.g., \"SELECT id FROM users WHERE email = '{{{email}}}'\")."},"extract":{"type":"string","description":"JSONPath-style expression that selects the value from the\nquery result row (e.g. `id`, `details.price`). Omit when\nthe query returns a single scalar."},"_lookupCacheId":{"type":"string","format":"objectId","description":"Optional reference to a LookupCache resource. When set, the\nlookup reads from the cache instead of issuing a live SQL\nquery — useful for stable reference data that should be\npre-loaded."},"allowFailures":{"type":"boolean","description":"When true, a lookup miss (no matching row and no `default`)\nresolves to `null` and the import continues. When false\n(default), a miss halts the record."},"map":{"type":"object","description":"Optional static key→value object evaluated BEFORE the SQL\nquery. When the incoming value matches a key, the mapped\nvalue is returned without querying the database."},"default":{"type":"string","description":"Value returned when the query matches no rows. When omitted\nand `allowFailures` is false, an unmatched lookup halts the\nrecord."}}},"description":"Lookup definitions executed against the JDBC connection. Each entry\nruns a `SELECT` and extracts a value used by field mappings\n(referenced via `lookupName`) or Handlebars expressions\n(referenced via `{{lookup \"name\" value}}`)."}}},"Mongodb":{"type":"object","description":"Configuration for MongoDB imports. Contains only the MongoDB-specific properties; import-level fields such as ignoreExisting, ignoreMissing, name, and description are set on the parent import, not here.","required":["method","collection"],"properties":{"method":{"type":"string","enum":["insertMany","updateOne"],"description":"Write operation performed against the MongoDB collection for each record."},"collection":{"type":"string","description":"MongoDB collection that receives the imported records.\nCollection names are case-sensitive and must not start with the reserved \"system.\" prefix."},"filter":{"type":"string","description":"MongoDB query filter that selects the document each updateOne applies to; required when\nmethod is updateOne. Supports standard MongoDB query operators and dot notation, and\nhandlebars placeholders to reference the incoming record's fields."},"document":{"type":"string","description":"Content of the document written to the collection (used when method is insertMany), as a JSON string that can include nested documents and arrays and handlebars placeholders."},"update":{"type":"string","description":"Modifications applied to matching documents (used when method is updateOne), using MongoDB update operators such as $set, $inc, or $push.\nA complete document without operators replaces the matched document entirely."},"upsert":{"type":"boolean","description":"When true, inserts a new document built from the update criteria if no existing document matches the filter.\nWhen false, records with no matching document are skipped and nothing is inserted."},"ignoreExtract":{"type":"string","description":"Path to the field in the source record that identifies existing records. When that field has a value, the record is treated as existing and skipped.\nOnly used when the import-level ignoreExisting flag is true; do not set it otherwise.\nEnclose field names containing special characters in square brackets (for example, [vendor-code]), and reference array items with an index, such as items[0].id."},"ignoreLookupFilter":{"type":"string","description":"JSON-stringified MongoDB query filter used to find existing documents in the target collection when the import-level ignoreExisting flag is true.\nIf the query matches a document, the incoming record is treated as existing and skipped.\nUse Handlebars placeholders to reference values from the incoming record, for example \"{\\\"email\\\":\\\"{{email}}\\\"}\".\nUnlike ignoreExtract, which only checks whether a field on the incoming record has a value, this filter queries the MongoDB collection itself."}},"if":{"properties":{"method":{"const":"updateOne"}},"required":["method"]},"then":{"required":["filter"]}},"NetSuite":{"type":"object","description":"Configuration for NetSuite imports (legacy adaptor; use netsuite_da / NetSuiteDistributedImport for SuiteApp 2.0).","properties":{"lookups":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique name for this lookup, referenced from mappings and handlebars templates with `{{lookup 'name' value}}`."},"recordType":{"type":"string","description":"NetSuite record type searched by a dynamic lookup (for example, `customer`, `salesOrder`). Set with `searchField` or `expression`; omit for a static `map` lookup."},"searchField":{"type":"string","description":"NetSuite field the dynamic lookup searches on (for example, `externalid`). Must be a searchable field on `recordType`. Use this or `expression`."},"expression":{"type":"string","description":"NetSuite search expression evaluated at runtime to select matching records (for example, `[\"recipient\",\"is\",\"123\"]`). Use this or `searchField`."},"resultField":{"type":"string","description":"Field on the matched NetSuite record whose value the lookup returns (for example, `internalid`).\nSupports dot notation for nested fields."},"map":{"type":["object","null"],"description":"Static lookup table with input values as keys and their corresponding output values. Use instead of a NetSuite search for fixed value translations. The platform stores `null` here on search-driven lookups."},"default":{"type":["string","null"],"description":"Fallback value returned when the lookup finds no match. Used together with `allowFailures`. Stored as `null` when not configured."},"allowFailures":{"type":"boolean","description":"When true, an unmatched lookup falls back to `default` and processing continues; when false, an unmatched lookup fails the record."},"_id":{"type":"object","description":"Server-assigned unique identifier for this lookup entry.","readOnly":true}}},"description":"Lookup definitions used to resolve reference values during the import, using either a static `map` or a dynamic NetSuite record search."},"operation":{"type":"string","enum":["add","update","addupdate","attach","detach","delete"],"description":"Operation to perform on NetSuite records, as a plain string (the API lowercases the value).\nFor `update`, `addupdate`, and `delete`, also set internalIdLookup so existing records can be found; for `add` with ignoreExisting, set internalIdLookup to check for duplicates before creating.\nDefault to `addupdate` when the intent is \"sync\", \"upsert\", or \"create or update\"; use `add` for plain \"create\" or \"insert\".\nDo not wrap the value in an object like `{\"type\": \"addupdate\"}` — that fails validation."},"isFileProvider":{"type":"boolean","description":"When true, enables file operations against NetSuite — browsing, uploading, downloading, updating, and deleting files.\nWhen false or omitted, file-related operations are unavailable through this import."},"customFieldMetadata":{"type":"object","description":"Metadata describing the custom fields defined on the target NetSuite record type, used to validate and process custom field data during the import."},"recordType":{"type":"string","description":"NetSuite record type this import writes to (e.g. customer, salesOrder, invoice).\nUse the exact NetSuite internal record type identifier; both standard and custom record types are supported."},"recordTypeId":{"type":"string","description":"The unique identifier specifying the record type within NetSuite. Determines the applicable schema, fields, validation rules, and processing logic. Use exact NetSuite record type identifiers such as standard types or custom record IDs."},"retryUpdateAsAdd":{"type":"boolean","description":"When true, an update that fails because the target record does not exist is automatically retried as an add.\nWhen false or unset, update failures error immediately without retrying.\nMay create duplicate records if the update failed for a reason other than a missing record."},"batchSize":{"type":"number","description":"Number of records sent to NetSuite per API call.\nLarger batches reduce the number of calls but increase memory use and timeout risk per call."},"internalIdLookup":{"type":"object","properties":{"extract":{"type":"string","description":"Path used to extract the value for the internal ID lookup."},"searchField":{"type":"string","description":"The NetSuite record field used as the key attribute for the internal ID lookup search. Should be a unique or indexed field to ensure accurate matches. Must correspond to a valid, searchable field on the target record type."},"expression":{"type":"string","description":"NetSuite search expression that filters which records match the lookup.\nSupports logical operators (AND, OR, NOT), comparison operators, and nested conditions, evaluated at runtime against current data."}},"description":"Configuration for locating existing NetSuite records by internal ID.\nRequired when operation is `update`, `addupdate`, or `delete`; also used with `add` plus ignoreExisting to check for duplicates."},"preferences":{"type":"object","properties":{"ignoreReadOnlyFields":{"type":"boolean","description":"When true, silently skips read-only fields during update operations instead of raising errors."},"warningAsError":{"type":"boolean","description":"When true, treats NetSuite warnings as errors, causing the operation to fail immediately."},"skipCustomMetadataRequests":{"type":"boolean","description":"When true, skips custom metadata requests during NetSuite API operations to improve performance."}},"description":"Preferences that control how NetSuite handles the import operation."},"file":{"type":"object","properties":{"name":{"type":"string","description":"Filename for the file in the NetSuite File Cabinet, without any folder or path information.\nMust be unique within its folder; include the file extension (e.g. .pdf, .csv)."},"fileType":{"type":"string","description":"NetSuite file type that governs how the file is processed, stored, and displayed.\nMust match the actual content format of the file."},"folder":{"type":"string","description":"Folder in the NetSuite File Cabinet where the file is stored, as a numeric folder ID or a folder path.\nUpdating this on an existing file moves the file to the specified folder."},"folderInternalId":{"type":"string","description":"Internal ID of the File Cabinet folder where the file is stored.\nMust reference an existing folder; changing it moves the file to a different folder."},"internalId":{"type":"string","description":"NetSuite internal ID of an existing file, used to target it for retrieval, updates, or deletion.\nAssigned by NetSuite when the file is created and cannot be changed."},"backupFolderInternalId":{"type":"string","description":"Internal ID of the File Cabinet folder where backup files are stored."}},"description":"Configuration for the file to upload to or update in the NetSuite File Cabinet."}},"if":{"not":{"propertyNames":{"enum":["preferences","lookups"]}}},"then":{"required":["operation"],"if":{"properties":{"isFileProvider":{"const":true}},"required":["isFileProvider"]},"else":{"required":["recordType"]}}},"NetsuiteDistributed":{"type":"object","description":"Configuration for NetSuite Distributed (SuiteApp 2.0) import operations — the primary sub-schema for the NetSuiteDistributedImport adaptorType. The API field name is \"netsuite_da\".\noperation and recordType are required. The server may attach empty bookkeeping stubs of\nthis object (empty lookups/mapping, or missingOrCorruptedDAConfig) to imports of other\nadaptor types; stubs omit these fields. Set internalIdLookup so existing records\ncan be found when operation is update, addupdate, or delete.","properties":{"operation":{"type":"string","enum":["add","update","addupdate","attach","detach","delete"],"description":"Operation to perform on the target NetSuite record, as a plain string. Required.\nFor `update`, `addupdate`, and `delete`, also set internalIdLookup so existing records can be found."},"recordType":{"type":"string","description":"NetSuite record type to import into (e.g. \"customer\", \"salesorder\", \"customrecord_myrecord\" for custom record types). Required.\nMust match a valid NetSuite record type identifier."},"recordIdentifier":{"type":"string","description":"Custom record identifier, used to identify the specific record type when\nimporting into custom record types.\n"},"restletVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"Version of the NetSuite RESTlet to use, as a plain string.\nDefaults to \"suiteapp2.0\" when useSS2Restlets is true, \"suitebundle\" otherwise; rarely needs to be set explicitly for modern integrations.\nThe version is fixed when the step is created — the Advanced selector is disabled on existing steps, and migrating an existing step to a different version means recreating or cloning it."},"useSS2Restlets":{"type":"boolean","description":"When true, uses SuiteScript 2.0 RESTlets and restletVersion defaults to \"suiteapp2.0\".\nDefaults to true for modern integrations.\nSet at step creation together with restletVersion — the same creation-time-only constraint applies (see restletVersion)."},"missingOrCorruptedDAConfig":{"type":"boolean","description":"When true, the Distributed Adaptor configuration is missing or corrupted.\nSet by the system — do not set manually."},"batchSize":{"type":"number","description":"Number of records to process per batch. Controls how many records are sent\nto NetSuite in a single API call. Typical values: 50-200.\n"},"internalIdLookup":{"type":"object","description":"Configuration for looking up existing NetSuite records to match against incoming data.\nRequired when operation is \"update\", \"addupdate\", or \"delete\".","properties":{"extract":{"type":"string","description":"Path in the source record to extract the lookup value from (e.g. \"internalId\" or \"externalId\")."},"searchField":{"type":"string","description":"NetSuite field to search against (e.g. \"externalId\", \"email\", \"name\", \"tranId\")."},"operator":{"type":"string","description":"Comparison operator for the lookup (e.g. \"is\", \"contains\", \"startswith\")."},"expression":{"type":"string","description":"NetSuite search expression for complex lookup conditions, used for multi-field or conditional lookups."}}},"hooks":{"type":"object","description":"Script hooks for custom processing at different stages of the import.\nEach hook references a SuiteScript file and function.\n","properties":{"preMap":{"type":"object","description":"Runs before field mapping is applied.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}},"postMap":{"type":"object","description":"Runs after field mapping, before submission to NetSuite.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}},"postSubmit":{"type":"object","description":"Runs after the record is submitted to NetSuite.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}}}},"mapping":{"type":"object","description":"NetSuite-specific Mapper 1.0 field mappings (the \"DA\" dialect).\nUsed by `NetSuiteDistributedImport` exclusively — NetSuite's\nDistributed Adaptor cannot consume generic v1 mappings or the\nMapper 2.0 shape because it needs sublist, subrecord, and\nconditional lookup semantics the generic schemas don't model.\n\nField-level shape: `fields` for body columns, `lists` for\nsublists / line-item arrays.","properties":{"fields":{"type":"array","items":{"type":"object","properties":{"extract":{"type":["string","null"],"description":"Path in the source record to extract the value from.\nUse dot notation for nested fields (e.g., \"address.city\").\nThe platform stores `null` here on fields driven by\n`hardCodedValue` instead of an extract path.\n"},"generate":{"type":"string","description":"The NetSuite field ID to write the value to (e.g., \"companyname\", \"email\", \"subsidiary\").\n"},"hardCodedValue":{"type":"string","description":"A static value to always use instead of extracting from source data.\nMutually exclusive with extract.\n"},"lookupName":{"type":"string","description":"Reference to a lookup defined in netsuite_da.lookups by name."},"dataType":{"type":"string","description":"Data type hint for the field value (e.g., \"string\", \"number\", \"date\", \"boolean\").\n"},"internalId":{"type":"boolean","description":"When true, the value is a NetSuite internal ID reference."},"immutable":{"type":"boolean","description":"When true, this field is only set on record creation, not on updates."},"discardIfEmpty":{"type":"boolean","description":"When true, skip this field mapping if the extracted value is empty."},"extractDateFormat":{"type":"string","description":"Date format of the extracted value (e.g., \"MM/DD/YYYY\", \"ISO8601\").\nUsed to parse date strings from source data.\n"},"extractDateTimezone":{"type":["string","null"],"description":"Timezone of the extracted date value (e.g., \"America/New_York\"). Stored as `null` when not configured."},"subRecordMapping":{"type":"object","description":"Nested Mapper 1.0 mapping for a NetSuite subrecord\nembedded in this field.  A subrecord is a child\nrecord that only exists inside a parent (e.g. the\nInventory Detail on a serialized-item line of a\nsales order); NetSuite reaches it through the\nparent.\n\n**Recursive structure.**  The nested `mapping`\nobject has the same `{fields, lists}` shape as the\ntop-level `netsuite_da.mapping`.  Recursion is\nallowed but rare.","properties":{"recordType":{"type":"string","description":"NetSuite's internal record-type id of the\nSUBRECORD (not the parent).  E.g.\n`inventorydetail`, `itempricing`,\n`mainaddress`.\n"},"jsonPath":{"type":"string","description":"JSONPath into the source record that selects the\ndata feeding this subrecord.  Semantics depend\non placement:\n* Line-level (on `lists[].fields[].`\n  `subRecordMapping`) — often `$` to reuse\n  the enclosing sublist line's row, or a path\n  into a nested array on that row.\n* Body-level (on `fields[].subRecordMapping`)\n  — a path into the source record root.\nDefaults to `$`.\n"},"mapping":{"type":"object","description":"Nested `{fields, lists}` mapping for the\nsubrecord.  Identical shape to the top-level\n`netsuite_da.mapping`.  Subrecords often have\nempty `fields` and only a sublist (e.g.\n`inventorydetail` → `inventoryassignment`).\n"},"lookups":{"type":"array","description":"Subrecord-scoped lookups.  Same shape as the\ntop-level `netsuite_da.lookups`.  Fields inside\nthis `subRecordMapping.mapping` reference them\nvia `lookupName`.  Often `[]`.\n"},"referenceFieldId":{"type":"string","description":"Secondary reference-field identifier used on a\nsmall fraction of subrecord blocks in production\n(~3%).  Exact semantics are not publicly\ndocumented by Celigo — match the value on\nexisting resources when present; do NOT\nsynthesize one.  Leave unset unless a template\nclearly requires it.\n"}}},"conditional":{"type":"object","description":"Conditional logic for when to apply this field mapping\n(\"Only perform mapping when\" in the UI). Lookup-based\nconditions evaluate the lookup named by `lookupName`,\nwhich must be defined on this import.\n","properties":{"lookupName":{"type":"string","description":"Lookup to evaluate for the condition. Must exactly match the\n`name` of a lookup defined on this import."},"when":{"type":"string","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","ignore_if_set"],"description":"Condition that controls when this field mapping is applied."}}}}},"description":"Body-level field mappings. Each entry maps a source field to a NetSuite body field.\n"},"lists":{"type":"array","items":{"type":"object","properties":{"generate":{"type":"string","description":"The NetSuite sublist ID (e.g., \"item\" for sales order line items,\n\"addressbook\" for address sublists).\n"},"jsonPath":{"type":"string","description":"LEGACY — do not set. Nominally the JSON path to the array of sublist records, but NetSuite DA ignores a top-level jsonPath here."},"fields":{"type":"array","items":{"type":"object","properties":{"extract":{"type":["string","null"],"description":"Path from the source record root that produces\nthe value for this sublist column. Paths are\nalways anchored at the source record root —\nnever relative to a pre-iterated row. The\nplatform stores `null` here on fields driven by\n`hardCodedValue` instead of an extract path."},"generate":{"type":"string","description":"NetSuite sublist field ID to write to."},"hardCodedValue":{"type":"string","description":"Static value for this sublist field."},"lookupName":{"type":"string","description":"Reference to a lookup by name."},"dataType":{"type":"string","description":"Data type hint for the field value."},"internalId":{"type":"boolean","description":"When true, the value is a NetSuite internal ID reference."},"isKey":{"type":"boolean","description":"When true, this field is a key field for matching existing sublist lines."},"immutable":{"type":"boolean","description":"When true, this field is only set on record creation, not on updates."},"discardIfEmpty":{"type":"boolean","description":"When true, skips this field mapping if the extracted value is empty."},"extractDateFormat":{"type":"string","description":"Date format of the extracted value."},"extractDateTimezone":{"type":["string","null"],"description":"Timezone of the extracted date value. Stored as `null` when not configured."},"subRecordMapping":{"type":"object","description":"Nested Mapper 1.0 mapping for a NetSuite\nsubrecord embedded in this sublist-field.  A\nsubrecord is a child record that only exists\ninside a parent (e.g. the Inventory Detail on\na serialized-item line of a sales order);\nNetSuite reaches it through the parent.\n\n**Recursive structure.**  The nested\n`mapping` object has the same\n`{fields, lists}` shape as the top-level\n`netsuite_da.mapping`.  Recursion is allowed\nbut rare.","properties":{"recordType":{"type":"string","description":"NetSuite's internal record-type id of the\nSUBRECORD (not the parent).  E.g.\n`inventorydetail`, `itempricing`,\n`componentinventorydetail`.\n"},"jsonPath":{"type":"string","description":"JSONPath (relative to the current sublist\nline) to the rows that populate the\nsubrecord.  Typically `$` when the\nsubrecord data is carried on the same\nline item (the enclosing\n`lists[].jsonPath` has already selected\nthe line).  Defaults to `$`.\n"},"mapping":{"type":"object","description":"Nested `{fields, lists}` mapping for the\nsubrecord.  Identical shape to the\ntop-level `netsuite_da.mapping`.\nSubrecords often have empty `fields` and\nonly a sublist (e.g. `inventorydetail` →\n`inventoryassignment`).\n"},"lookups":{"type":"array","description":"Subrecord-scoped lookups.  Same shape as\nthe top-level `netsuite_da.lookups`.\nFields inside this\n`subRecordMapping.mapping` reference\nthem via `lookupName`.  Often `[]`.\n"},"referenceFieldId":{"type":"string","description":"Secondary reference-field identifier used\non a small fraction of subrecord blocks\nin production (~3%).  Exact semantics are\nnot publicly documented by Celigo — match\nthe value on existing resources when\npresent; do NOT synthesize one.  Leave\nunset unless a template clearly requires\nit.\n"}}},"conditional":{"type":"object","description":"Conditional logic for when to apply this sublist field mapping\n(\"Only perform mapping when\" in the UI). Lookup-based conditions\nevaluate the lookup named by `lookupName`, which must be defined\non this import.","properties":{"lookupName":{"type":"string","description":"Lookup to evaluate for the condition. Must exactly match the\n`name` of a lookup defined on this import."},"when":{"type":"string","description":"Condition that controls when this field mapping is applied.","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","ignore_if_set"]}}}}},"description":"Field mappings for each column in the sublist."}}},"description":"Sublist (line-item) mappings. Each entry maps source data to a NetSuite sublist.\n"}}},"lookups":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia `lookupName` and Handlebars expressions reference it via\n`{{lookup \"name\" value}}`. Must be unique within this\nresource's `lookups` array."},"recordType":{"type":"string","description":"NetSuite record type to search. Use the internal record-type\nid (lowercase, underscored), e.g. `customer`, `item`,\n`salesorder`, `customlist_xxx`."},"searchField":{"type":"string","description":"NetSuite field on `recordType` to match against. Use the\ninternal field id — e.g. `email`, `externalid`,\n`internalid`, `entityid`, `custitem_xxx`."},"resultField":{"type":"string","description":"NetSuite field whose value is returned when a record matches.\nTypically `internalid` when the lookup resolves to a record\nreference, or any queryable scalar field."},"expression":{"type":"string","description":"Optional advanced-search expression (NetSuite saved-search\nDSL) for multi-criterion lookups. When present, supersedes\n`searchField` / `operator` — those fields are ignored."},"operator":{"type":"string","description":"Comparison operator for the `searchField` match. Common\nvalues: `is`, `contains`, `startswith`, `anyof`,\n`equalto`, `greaterthan`."},"includeInactive":{"type":"boolean","description":"When true, includes inactive records in lookup results."},"useDefaultOnMultipleMatches":{"type":"boolean","description":"Controls behaviour when the search matches more than one\nrecord. When true, the `default` value is returned instead\nof raising an ambiguous-match error. When false (default),\nmultiple matches are treated as a failure."},"allowFailures":{"type":["boolean","null"],"description":"When true, a lookup miss (no matching record and no\n`default`) resolves to `null` and the import continues.\nWhen false (default), a miss halts the record. May be\nstored as null (treated as unset)."},"map":{"type":["object","null"],"description":"Optional static key→value object evaluated BEFORE the\nNetSuite search. When the incoming value matches a key, the\nmapped value is returned without hitting NetSuite. Null on\ndynamic (search-based) lookups with no static map."},"default":{"type":["string","null"],"description":"Value returned when the search matches no records. When\nomitted and `allowFailures` is false, an unmatched lookup\nhalts the record. May be stored as null (no default)."}}},"description":"Lookup definitions that resolve reference values from NetSuite.\nReferenced by name from field mappings via `lookupName` and from\nHandlebars expressions via `{{lookup \"name\" value}}`."},"rawOverride":{"type":"object","description":"Raw override object for advanced use cases. When useRawOverride is true,\nthis object is sent directly to the NetSuite API, bypassing normal mapping.\n"},"useRawOverride":{"type":"boolean","description":"When true, uses rawOverride instead of the normal mapping configuration.\n"},"isMigrated":{"type":"boolean","description":"When true, this import was migrated from a legacy format. Set by the system."},"retryUpdateAsAdd":{"type":"boolean","description":"When true, if an update fails because the record doesn't exist, automatically\nretry as an add operation. Useful for initial syncs where records may not exist yet.\n"},"customFieldMetadata":{"type":"object","description":"Metadata about custom fields on the target record type.\nPopulated by the system from NetSuite metadata — do not set manually.\n"},"file":{"type":"object","description":"File cabinet configuration for file-based imports into NetSuite.\n","properties":{"name":{"type":"string","description":"Filename for the file in NetSuite File Cabinet."},"fileType":{"type":"string","description":"NetSuite file type (e.g., \"PDF\", \"CSV\", \"PLAINTEXT\", \"EXCEL\", \"XML\").\n"},"folder":{"type":"string","description":"Folder path or name in the NetSuite File Cabinet."},"folderInternalId":{"type":"string","description":"Internal ID of the target folder in NetSuite File Cabinet."},"internalId":{"type":"string","description":"Internal ID of an existing file to update."},"backupFolderInternalId":{"type":"string","description":"Internal ID of a backup folder for file versioning."}}},"isFileProvider":{"type":"boolean","description":"Whether this import handles files in the NetSuite File Cabinet.\n"},"preferences":{"type":"object","properties":{"ignoreReadOnlyFields":{"type":"boolean","description":"When true, silently skip read-only fields instead of raising errors."},"warningAsError":{"type":"boolean","description":"When true, treat NetSuite warnings as errors that stop the import."},"skipCustomMetadataRequests":{"type":"boolean","description":"When true, skip fetching custom field metadata to improve performance."}},"description":"Import behavior preferences that control how NetSuite handles the import operation.\n"},"recordTypeId":{"type":"string","description":"The internal record type ID. Used for custom record types in NetSuite where\nthe numeric ID is needed in addition to the recordType string.\n"}},"if":{"not":{"propertyNames":{"enum":["lookups","mapping","missingOrCorruptedDAConfig"]}}},"then":{"required":["operation","recordType"],"if":{"properties":{"operation":{"enum":["update","addupdate","delete"]}},"required":["operation"]},"then":{"required":["internalIdLookup"]}}},"Rdbms":{"type":"object","description":"Configuration for RDBMS imports into SQL Server, MySQL, PostgreSQL, Snowflake, Oracle, MariaDB, and other relational databases.\nqueryType determines which companion field is required: per_record, per_page, and first_page use the query field; bulk_insert uses the bulkInsert object; bulk_load uses the bulkLoad object — never set query and bulkInsert together.\n(The server itself stores an empty `query: []` next to bulk_insert/bulk_load configs and may leave an empty `bulkInsert: {}` stub next to others — empty companions are normal in responses; only a populated conflicting companion is invalid.)\nThe query field is an array of plain SQL strings, not a single string and not an array of objects.","properties":{"lookups":{"$ref":"#/components/schemas/Lookups","description":"Lookup definitions executed against the RDBMS connection. Each\nentry runs a `SELECT` and extracts a value used by field mappings\n(referenced via `lookupName`) or Handlebars expressions\n(referenced via `{{lookup \"name\" value}}`).\n\n**Mirror invariant**\nThe Celigo platform persists lookups in TWO locations on an RDBMS\nimport: top-level `lookups` and `rdbms.lookups`. Both arrays\nmust hold byte-for-byte identical content — the MappingsAgent\nwrites both on every save.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia `lookupName` and Handlebars expressions reference it via\n`{{lookup \"name\" value}}`. Must be unique within this\nresource's `lookups` array.\n"},"query":{"type":"string","description":"SQL `SELECT` statement that resolves the lookup value.\nTypically returns a single row with a single column; use\n`extract` to pick the column when the query returns multiple.\nInject values from the incoming record with Handlebars — RDBMS\nqueries use triple braces and the `record.` prefix:\n`SELECT id FROM users WHERE email = '{{{record.email}}}'`.\n"},"extract":{"type":"string","description":"JSONPath-style expression that selects the value from the query\nresult row (e.g. `id`, `details.price`). Omit when the\nquery returns a single scalar — the first column is used\nautomatically.\n"},"_lookupCacheId":{"type":"string","format":"objectId","description":"Optional reference to a LookupCache resource. When set, the\nlookup reads from the cache instead of issuing a live SQL\nquery — useful for stable reference data that should be\npre-loaded.\n"},"map":{"type":"object","description":"Optional static key→value object evaluated BEFORE the SQL query.\nWhen the incoming value matches a key, the mapped value is\nreturned without querying the database. Useful for small\ncontrolled enumerations.\n"},"default":{"type":"string","description":"Value returned when the query matches no rows. When omitted and\n`allowFailures` is false, an unmatched lookup halts the\nrecord.\n"},"allowFailures":{"type":"boolean","description":"When true, a lookup miss (no matching row and no `default`)\nresolves to `null` and the import continues. When false\n(default), a miss halts the record.\n"}}},"type":"array"},"query":{"type":"array","items":{"type":"string"},"description":"SQL statements to execute; required when queryType is `[\"per_record\"]`, `[\"per_page\"]`, or `[\"first_page\"]`, and typically contains a single statement.\nEach element is a complete SQL statement as a plain string — never a single bare string and never an array of objects (objects cause a Cast error).\nInject values from the incoming record with Handlebars using the `record.` prefix and triple braces, e.g. `'{{{record.name}}}'` for strings (single-quoted in the SQL) and `{{{record.quantity}}}` for numbers.\nRendering is strict: a reference the render model cannot resolve (a bare `{{name}}` without prefix, or `record.*` when the arriving record is a grouped array) fails the step with `cannot_evaluate_handlebars` — it does not render an empty value.\n`record` and `rows` are aliases chosen by the arriving data's shape: an object record binds `record` (`rows` undefined); a grouped array-record (e.g. upstream `groupByFields`) binds `rows` (`record` undefined) — address grouped rows as `{{rows.0.field}}` or iterate `{{#each rows}}`.\nDouble braces (`{{record.field}}`) output the value wrapped in single quotes with embedded quotes doubled, while triple braces output it raw; block helpers such as `{{#each}}` and `{{#if}}` use double braces as normal."},"queryType":{"type":["array","null"],"items":{"type":"string","enum":["INSERT","UPDATE","bulk_insert","per_record","per_page","first_page","bulk_load"]},"description":"Execution strategy for the SQL operation, which determines the required companion field: per_record, per_page, and first_page require `query`; bulk_insert requires `bulkInsert`; bulk_load requires `bulkLoad`.\nPrefer the highest-performance type the operation supports: bulk_load (currently Snowflake and NSAW — handles INSERT, upsert via `bulkLoad.primaryKeys`, and custom merge or ignore-existing logic via `bulkLoad.overrideMergeQuery`), then bulk_insert for pure INSERTs, then per_page, then per_record only when the logic cannot be expressed as a batch operation.\nFor UPDATE, UPSERT, or ignore-existing logic on databases without bulk_load support, use per_page or per_record with `query` — bulk_insert has no duplicate-checking logic.\nSet a single value; do not combine types or use the legacy INSERT and UPDATE values.\nper_page statements render against the whole batch (`batch_of_records`) rather than a single record — loop with `{{#each batch_of_records}}...{{{record.fieldName}}}...{{/each}}`.\n\n`bulk_load` — Stages data as a file and loads via database-native COPY/bulk mechanism for maximum throughput; supports INSERT, upsert via bulkLoad.primaryKeys, and custom merge or ignore-existing logic via bulkLoad.overrideMergeQuery; currently supported for Snowflake and NSAW.\n`bulk_insert` — Batch INSERT via multi-row VALUES clause for efficient bulk data loading; has no upsert or ignore-existing logic and is available for all RDBMS types.\n`per_page` — Executes one SQL statement per page (batch) of records; available for all RDBMS types.\n`per_record` — Executes one SQL statement per record, giving full control over individual record SQL; available for all RDBMS types."},"bulkInsert":{"type":"object","description":"Configuration for batch INSERTs via a multi-row VALUES clause; required when queryType is `[\"bulk_insert\"]` and must not be set otherwise.\nOnly suited to pure INSERTs — for UPDATE, UPSERT, or ignore-existing/skip-duplicates logic, use queryType `[\"per_record\"]` with the `query` field instead.","properties":{"tableName":{"type":"string","description":"The name of the database table into which the bulk insert operation will be executed. This value must correspond to a valid, existing table within the target relational database management system (RDBMS). It serves as the primary destination for inserting multiple rows of data efficiently in a single operation. The table name can include schema or namespace qualifiers if supported by the database (e.g., \"schemaName.tableName\"), allowing precise targeting within complex database structures. Proper validation and sanitization of this value are essential to ensure the operation's success and to prevent SQL injection or other security vulnerabilities."},"batchSize":{"type":"string","description":"The number of records to be inserted into the database in a single batch during a bulk insert operation. This parameter is crucial for optimizing the performance and efficiency of bulk data loading by controlling how many records are grouped together before being sent to the database. Proper tuning of batchSize balances memory consumption, transaction overhead, and throughput, enabling the system to handle large volumes of data efficiently without overwhelming resources or causing timeouts. Adjusting batchSize directly impacts transaction size, network utilization, error handling granularity, and recovery strategies, making it essential to tailor this value based on the specific database capabilities, system resources, and workload characteristics."}}},"bulkLoad":{"type":"object","properties":{"tableName":{"type":"string","description":"Target table for the bulk load.\nMust reference an existing table accessible with the connection's credentials; schema-qualified names are supported."},"primaryKeys":{"type":"array","items":{"type":"string"},"description":"Columns that uniquely identify each record in the target table, used to match existing rows during the load.\nSet for upsert behavior (a MERGE is auto-generated); omit (or empty) for a pure INSERT.\nComposite keys are supported — list every key column, and names must exactly match the target schema."},"overrideMergeQuery":{"type":"boolean","description":"When true, a custom merge query replaces the auto-generated MERGE statement, enabling ignore-existing logic, conditional updates, or multi-table operations.\nThe override SQL references `{{import.rdbms.bulkLoad.preMergeTemporaryTable}}` for the staging table."}},"description":"Configuration for bulk loading: data is staged as a file and loaded via the database's native COPY/bulk mechanism for maximum throughput.\nRequired when queryType is `[\"bulk_load\"]`; currently supported for Snowflake and NSAW."},"updateLookupName":{"type":["string","null"],"description":"Name of the lookup used for update operations in the legacy composite (queryType1) flow."},"updateExtract":{"type":["string","null"],"description":"Path used to extract the value that drives update operations in the legacy composite flow."},"ignoreLookupName":{"type":["string","null"],"description":"Name of the lookup used to determine which records to ignore when ignore-existing is enabled."},"ignoreExtract":{"type":["string","null"],"description":"Path used to extract the value that determines whether a record is ignored when ignore-existing is enabled."}},"if":{"not":{"propertyNames":{"enum":["lookups"]}}},"then":{"required":["queryType"],"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"enum":["per_record","per_page"]}}}},"then":{"required":["query"],"properties":{"bulkInsert":{"maxProperties":0}}},"else":{"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"const":"first_page"}}}},"then":{"required":["query"]},"else":{"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"const":"bulk_insert"}}}},"then":{"required":["bulkInsert"],"properties":{"query":{"maxItems":0}}},"else":{"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"const":"bulk_load"}}}},"then":{"required":["bulkLoad"],"properties":{"query":{"maxItems":0}}}}}}}},"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 set to true\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\nIf allowFailures is false and no default is specified, the flow will fail\nwhen encountering unmapped values.\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\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\nThe best practice is typically to set allowFailures to true with a meaningful\ndefault value, so flows remain operational while alerting you to missing mappings.\n"}}}},"S3":{"type":"object","description":"Defines where files are written to 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 destination, fileKey sets the object key, and backupBucket\nretains a copy after a successful import.","required":["region","bucket"],"properties":{"region":{"type":"string","default":"us-east-1","description":"AWS region where the S3 bucket resides. Must match the bucket's actual location to avoid\nconnectivity errors and misrouted requests."},"bucket":{"type":"string","description":"S3 bucket that receives the imported files. The bucket must already exist and the\nconnection's AWS credentials must have s3:PutObject permission on it."},"fileKey":{"type":"string","description":"Object key under which each file is stored in the bucket. Include slashes to organize\nfiles in a folder-style hierarchy (for example, imports/2023/12/orders.json); keys are\ncase-sensitive. Supports handlebars placeholders such as orders-{{timestamp}}.json to\ngenerate a unique key per run."},"backupBucket":{"type":"string","description":"S3 bucket where a copy of each imported file is retained after a successful import; if\nomitted, no backup copy is kept. Must reference an existing bucket the connection's\ncredentials have s3:PutObject permission on."},"serverSideEncryptionType":{"type":"string","description":"Server-side encryption applied to uploaded objects. The connection form sets `AES256`\n(SSE-S3, S3-managed keys) when encryption is enabled; leave unset to use the bucket's\ndefault encryption."}}},"Wrapper":{"type":"object","description":"Configuration for Wrapper imports, which delegate writing records 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.","properties":{"function":{"type":"string","description":"Name of the function the wrapper invokes to process records.\nMust match a callable function in the wrapper's execution context; names are case-sensitive."},"configuration":{"type":"object","additionalProperties":true,"description":"Free-form settings passed to the wrapper function at runtime (connector-specific keys such\nas method, apiVersion, headers, or handler). Structure is defined by the wrapper code, not\nby this schema."},"lookups":{"$ref":"#/components/schemas/Lookups","description":"Lookup definitions for this wrapper import. Wrapper lookups use\nthe generic Celigo shape: either a static `map` (key→value\nobject) or a dynamic `_lookupCacheId` + `extract` pair. See\n`common/schemas/lookups.yml` for the full item shape."}},"if":{"not":{"propertyNames":{"enum":["lookups"]}}},"then":{"required":["function"]}},"Salesforce":{"type":"object","description":"Salesforce-specific configuration for the import: the operation to perform, the API to use, and object-level settings such as `sObjectType`, `idLookup`, and `upsert`. Properties like `ignoreExisting`, `ignoreMissing`, `name`, and `description` are not part of this object — they belong at the import level.","properties":{"lookups":{"description":"Lookup definitions used to resolve Salesforce record references or\nstatic value mappings. Each lookup has a unique ``name`` that mapping\nfields reference via ``lookupName`` and that Handlebars expressions\nreference via ``{{lookup \"name\" value}}``.\n\n**Mirror invariant**\nThe Celigo platform stores lookups in TWO locations on a Salesforce\nimport resource: top-level ``lookups`` and ``salesforce.lookups``.\nBoth arrays must hold byte-for-byte identical content — the\nMappingsAgent writes both on every save.\n\n**Static vs dynamic lookups**\n- **Static** — only ``map`` + optional ``default``. No Salesforce\n  query runs. Use for small controlled vocabularies (country codes,\n  status labels, etc.).\n- **Dynamic** — ``sObjectType`` + ``resultField`` + ``whereClause``.\n  Issues a SOQL query at runtime. Use ``map`` alongside the query to\n  short-circuit known values before hitting Salesforce.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup entry. Mapping fields reference\nit via ``lookupName``, and Handlebars expressions reference it\nvia ``{{lookup \"name\" value}}``. Must be unique within this\nresource's ``lookups`` array. Conventionally a short slug or a\ngenerated hash — the value is only meaningful as a reference\ntoken.\n"},"sObjectType":{"type":"string","description":"Salesforce sObject API name to query (e.g. ``Account``,\n``Contact``, ``Solution__c``). For custom objects include the\n``__c`` suffix. Case-sensitive — must match exactly as declared\nin the target Salesforce org.\n"},"resultField":{"type":"string","description":"sObject field whose value is returned when the SOQL ``whereClause``\nmatches a record. Typically ``Id`` when the lookup resolves to a\nSalesforce record reference, but any queryable field is valid.\n"},"whereClause":{"type":"string","description":"SOQL ``WHERE`` clause that identifies the matching Salesforce\nrecord. Use Handlebars expressions (triple braces recommended:\n``{{{field}}}``) to inject values from the incoming source record.\nWrap the whole expression in parentheses.\n\nExamples:\n- ``(Id = {{{id [Customer SFDC ID]}}})``\n- ``(Email = '{{{email}}}')``\n- ``(External_Id__c = {{{string externalId}}} AND IsActive = true)``\n\nCeligo's Handlebars helpers (``string``, ``id``, ``double``,\n``bool``, ``date``) produce correctly-typed SOQL literals —\nprefer them over manual quoting. String values wrap in single\nquotes; numeric/id values do not.\n"},"map":{"type":["object","null"],"description":"Optional static key→value map evaluated BEFORE the SOQL query.\nWhen the incoming field value matches a key, the mapped value\nis returned without hitting Salesforce. Useful for small\ncontrolled enumerations where a full query would be overkill.\n"},"default":{"type":["string","null"],"description":"Value returned when the SOQL query matches no records. When\nomitted and ``allowFailures`` is false, an unmatched lookup\nfails the record.\n"},"allowFailures":{"type":"boolean","description":"When true, a lookup miss (no matching record and no ``default``)\nis tolerated — the mapped field resolves to ``null`` and the\nimport continues. When false (default), a miss halts processing\nfor the record.\n"},"useDefaultOnMultipleMatches":{"type":"boolean","description":"Controls behaviour when the SOQL query matches more than one\nrecord. When true, the ``default`` value is returned instead of\nraising an ambiguous-match error. When false (default), multiple\nmatches are treated as a failure.\n"}}},"type":"array"},"operation":{"type":"string","enum":["insert","update","upsert","upsertpicklistvalues","delete","addupdate"],"description":"Controls how records are written to Salesforce. Each value pairs with a lookup strategy: `upsert` requires `upsert.externalIdField` plus `idLookup.extract`, while `update`, `delete`, `addupdate`, and `insert` with `ignoreExisting` require `idLookup.whereClause`. Values are converted to lowercase automatically."},"api":{"type":"string","enum":["soap","rest","metadata","compositerecord"],"description":"Selects the Salesforce API used to execute the import. Use `soap` for most imports and another API only when its specific capability is needed; default to `soap` when no API is specified. Values are converted to lowercase automatically."},"soap":{"type":"object","properties":{"headers":{"type":"object","properties":{"allOrNone":{"type":"boolean","description":"When true, the batch is processed atomically: if any record fails, the entire transaction is rolled back and nothing is committed. When false or omitted, records that succeed are committed even if others in the batch fail."}},"description":"SOAP headers included in requests sent to Salesforce, such as the `allOrNone` transaction-control header."},"batchSize":{"type":"number","description":"Number of records sent to Salesforce in each SOAP API batch. Larger batches reduce the number of API calls; smaller batches reduce per-call load and the chance of timeouts."}},"description":"Settings that apply when `api` is `soap`, including SOAP request headers and batch size."},"sObjectType":{"type":"string","description":"Salesforce object the import writes to, such as `Account`, `Contact`, or a custom object like `Vendor__c`. Must exactly match the object's case-sensitive API name; custom objects include the `__c` suffix."},"idLookup":{"type":"object","properties":{"extract":{"type":"string","description":"Field in the incoming data whose value is matched against the Salesforce External ID field named in `upsert.externalIdField`. Set only when `operation` is `upsert`; all other operations use `whereClause` instead. Choose a field that is unique and always populated — missing or null values cause the upsert to fail. If both `extract` and `whereClause` are set, `extract` takes precedence for upsert."},"whereClause":{"type":"string","description":"SOQL condition (without the `WHERE` keyword) that finds existing Salesforce records for each incoming record. Required when `operation` is `update`, `delete`, or `addupdate`, and for `insert` with `ignoreExisting`; for `upsert`, use `extract` instead. Reference incoming fields with triple-brace Handlebars and wrap string values in single quotes (e.g. `Email = '{{{Email}}}'`) — triple braces emit the raw value without HTML-escaping, which would corrupt characters like `&` or `'`. Combine conditions with `AND`/`OR`."}},"description":"Controls how existing Salesforce records are matched before writing. Set exactly one of `extract` (for `upsert`) or `whereClause` (for `update`, `delete`, `addupdate`, and `insert` with `ignoreExisting`); omit for plain inserts.\n\nRequired when operation is addupdate, delete, update, or upsert."},"upsert":{"type":"object","properties":{"externalIdField":{"type":"string","description":"API name of the Salesforce field, marked as External ID, that upsert operations match against. This is the target side of the match; `idLookup.extract` names the source field in the incoming data. If the value matches multiple Salesforce records, the upsert fails."}},"description":"Upsert matching configuration, required when `operation` is `upsert` and omitted for all other operations. Contains only `externalIdField`, which pairs with `idLookup.extract` at the parent level.\n\nRequired when operation is upsert."},"upsertpicklistvalues":{"type":"object","properties":{"type":{"type":"string","enum":["picklist","multipicklist"],"description":"Picklist field type being managed. Values are converted to lowercase automatically."},"fullName":{"type":"string","description":"Fully qualified API name of the picklist field, in `ObjectName.FieldName__c` form (for example, `Account.MyPicklist__c`). Must match the case-sensitive Salesforce API name."},"label":{"type":"string","description":"Display label for the picklist field in the Salesforce UI. Can contain spaces and does not need to match the API name."},"visibleLines":{"type":"number","description":"Number of values visible without scrolling in the Salesforce UI selection box. Only applies when `type` is `multipicklist`."}},"description":"Picklist field definition managed when `operation` is `upsertpicklistvalues`; omit for all other operations. Manages the field's metadata — name, label, and display settings — and requires Salesforce Metadata API permissions.\n\nRequired when operation is upsertpicklistvalues."},"removeNonSubmittableFields":{"type":"boolean","description":"When true, strips fields Salesforce will not accept — read-only, system-generated, and formula fields — from the payload before submission, preventing rejection errors. When false or omitted, all fields are submitted as-is. Only the outgoing payload is affected; the source data is unchanged."},"document":{"type":"object","properties":{"id":{"type":"string","description":"Salesforce-assigned ID of the Document record. The 15-character form is case-sensitive; the 18-character form adds a case-insensitivity checksum."},"name":{"type":"string","description":"Display name of the document in Salesforce, used to identify it in folders, search, and API operations."},"folderId":{"type":"string","description":"Salesforce folder that stores the document. The document inherits the folder's sharing and visibility settings; changing this value moves the document to a different folder."},"contentType":{"type":"string","description":"MIME type of the document content, such as `application/pdf` or `image/png`."},"developerName":{"type":"string","description":"Unique API name used to reference the document in code, metadata, and integrations. Distinct from the display name; uses only letters, numbers, and underscores with no spaces."},"isInternalUseOnly":{"type":"boolean","description":"When true, marks the document for internal use only — it must not be shared with parties outside the organization."},"isPublic":{"type":"boolean","description":"When true, the document is accessible to all users; when false, access is limited to authorized users by Salesforce permissions and sharing settings."}},"description":"Salesforce Document record written by the import, combining the file content with metadata such as name, folder, and content type."},"attachment":{"type":"object","properties":{"id":{"type":"string","description":"Salesforce-assigned ID of the Attachment record. Prefer the 18-character case-insensitive form over the 15-character case-sensitive form in integrations."},"name":{"type":"string","description":"Filename of the attachment, including the file extension (for example, `contract_agreement.pdf`)."},"parentId":{"type":"string","description":"Salesforce record the attachment is linked to, such as an Account, Contact, or Opportunity. Required when creating an attachment; access to the attachment follows the parent record's permissions."},"contentType":{"type":"string","description":"MIME type of the attachment content, such as `application/pdf` or `image/png`."},"isPrivate":{"type":"boolean","description":"When true, the attachment is visible only to users explicitly authorized to access it; when false or omitted, it inherits the visibility of its parent record. This setting only restricts access further — it never grants access beyond the parent record's permissions."},"description":{"type":"string","description":"Free-text note describing the attachment's content or purpose, shown alongside the attachment details in Salesforce."}},"description":"Salesforce Attachment record written by the import — a file linked to a parent record via `parentId`, with metadata such as name and content type."},"contentVersion":{"type":"object","properties":{"contentDocumentId":{"type":"string","description":"ContentDocument this version belongs to; all versions of the same document share this ID. Assigned by Salesforce when the content version is created and immutable afterward."},"title":{"type":"string","description":"Display title of this content version, shown in content libraries, search results, and version history."},"pathOnClient":{"type":"string","description":"Original file path on the client machine before upload. Used for reference and display only; it does not affect how the file is stored or accessed in Salesforce."},"tagCsv":{"type":"string","description":"Comma-separated tags applied to the content version for categorization and search. Updating this field replaces the entire tag set, so include every tag the version should keep."},"contentLocation":{"type":"string","description":"Specifies where the content file for this ContentVersion record is stored — \"S\" for Salesforce internal storage or \"E\" for an external system (the two values the import form offers). Salesforce also defines \"L\" for content shared via a link, but the form does not set it. Typically assigned automatically based on the upload method."}},"description":"Salesforce ContentVersion record written by the import — a specific version of a content item in Salesforce Files. Each modification produces a new version, and all versions of a document are linked through `contentDocumentId`."}},"if":{"anyOf":[{"required":["attachment"]},{"required":["contentVersion"]}]},"then":{"required":["operation"]},"else":{"if":{"not":{"propertyNames":{"enum":["lookups","api"]}}},"then":{"required":["operation","api"],"if":{"properties":{"operation":{"const":"upsert"}},"required":["operation"]},"then":{"required":["idLookup","upsert"],"properties":{"idLookup":{"required":["extract"]},"upsert":{"required":["externalIdField"]}}},"else":{"if":{"properties":{"operation":{"enum":["update","delete","addupdate"]}},"required":["operation"]},"then":{"required":["idLookup"],"properties":{"idLookup":{"required":["whereClause"]}}},"else":{"if":{"properties":{"operation":{"const":"upsertpicklistvalues"}},"required":["operation"]},"then":{"required":["upsertpicklistvalues"],"properties":{"upsertpicklistvalues":{"required":["fullName"]}}}}}}}},"Tool":{"type":"object","description":"Configuration for a `ToolImport`, which executes a reusable Celigo Tool resource as the\nimport action. The referenced tool defines its own input contract, processing pipeline, and\noutput; this object binds the tool to the import and optionally overrides the connections the\ntool uses for this invocation.","required":["_toolId"],"properties":{"_toolId":{"type":"string","format":"objectId","description":"Tool resource this import executes. The tool supplies the input schema, routing, page\nprocessors, and output mapping; the import runs that tool when records reach it. Required —\na `ToolImport` has no behavior without a tool to run."},"overrides":{"type":"object","description":"Per-import overrides applied to the referenced tool at execution time. Tools use a\nbring-your-own-keys connection model: the tool declares abstract connection slots, and the\nconsumer binds concrete connections here so one tool definition can run against different\nsystems without being modified. Omit to run the tool with its default connections.","properties":{"connections":{"type":"array","description":"Connection bindings that replace the tool's abstract connection slots for this import.\nEach entry maps one of the tool's abstract connections to the concrete connection the\nimport should use.","items":{"type":"object","required":["_id","_abstractId"],"properties":{"_id":{"type":"string","format":"objectId","description":"Concrete connection bound to the abstract slot for this import's tool run."},"_abstractId":{"type":"string","format":"objectId","description":"Abstract connection slot, declared by the tool, that this binding replaces. Matches\nthe abstract connection id defined on the referenced tool."},"_borrowConcurrencyFromConnectionId":{"type":"string","format":"objectId","description":"Optional pointer to another connection whose\nconcurrency budget this binding shares. Use\nwhen several wrappers / steps should share rate\nlimits against the same downstream system but\nuse different credentials. Mutually exclusive\nwith `_id` on the same entry.\n"}}}}}}}},"File":{"type":"object","description":"**CRITICAL: This object is REQUIRED for all file-based import adaptor types.**\n\n**When to include this object**\n\n✅ **MUST SET** when `adaptorType` is one of:\n- `S3Import`\n- `FTPImport`\n- `AS2Import`\n- `HTTPImport` in file mode (`http.type: \"file\"` — the folder mode of the cloud file\n  providers such as Google Drive, Box, Dropbox, Azure Blob, Google Cloud Storage and Celigo\n  Storage); the same `file` object, including `type: filedefinition` with `_ediProfileId`\n  for EDI, applies as on S3 / FTP\n\n❌ **DO NOT SET** for non-file-based imports like:\n- `SalesforceImport`\n- `NetSuiteImport`\n- `HTTPImport` in records mode (`http.type` unset — a REST endpoint receiving records)\n- `MongodbImport`\n- `RDBMSImport`\n\n**Minimum required fields**\n\nFor most file imports, you need at minimum:\n- `fileName`: The output file name (supports Handlebars like `{{timestamp}}`)\n- `type`: The file format (json, csv, xml, xlsx)\n- `aggregation.type`: How the run's records are batched into files (`all` for one file per run)\n\n**Example (S3 Import)**\n\n```json\n{\n  \"file\": {\n    \"fileName\": \"customers-{{timestamp}}.json\",\n    \"aggregation\": { \"type\": \"all\" },\n    \"type\": \"json\"\n  },\n  \"s3\": {\n    \"region\": \"us-east-1\",\n    \"bucket\": \"my-bucket\",\n    \"fileKey\": \"customers-{{timestamp}}.json\"\n  },\n  \"adaptorType\": \"S3Import\"\n}\n```","properties":{"fileName":{"type":"string","description":"**REQUIRED for file-based imports.**\n\nThe name of the file to be created/written. Supports Handlebars expressions for dynamic naming.\n\n**Default behavior**\n- When the user does not specify a file naming convention, **always default to timestamped filenames** using `{{timestamp}}` (e.g., `\"items-{{timestamp}}.csv\"`). This ensures each run produces a unique file and avoids overwriting previous exports.\n- Only use a fixed filename (without timestamp) if the user explicitly requests overwriting or a fixed name.\n\n**Common patterns**\n- `\"data-{{timestamp}}.json\"` - Timestamped JSON file (DEFAULT — use this pattern when not specified)\n- `\"export-{{date}}.csv\"` - Date-stamped CSV file\n- `\"{{recordType}}-backup.xml\"` - Dynamic record type naming\n\n**Examples**\n- `\"customers-{{timestamp}}.json\"`\n- `\"orders-export.csv\"`\n- `\"inventory-{{date}}.xlsx\"`\n\n**Important**\n- For S3 imports, this should typically match the `s3.fileKey` value\n- For FTP imports, this should typically match the `ftp.fileName` value"},"skipAggregation":{"type":"boolean","description":"Legacy companion of `aggregation.type`: `true` when the run's records are written one\nfile per page, `false` when they are combined across the run or grouped by key.\nValidation reads the value sent — an EDI file definition needs `true` unless\n`aggregation.type` is `key`, and `batchSize` needs `true` — then the save rewrites it\nfrom `aggregation.type`, so send the two as a consistent pair.","default":false},"type":{"type":"string","enum":["json","csv","xml","xlsx","filedefinition"],"description":"**REQUIRED for file-based imports.**\n\nThe format of the output file.\n\nSelect the format that matches the target system's requirements."},"encoding":{"type":"string","enum":["utf8","win1252","utf-16le","gb18030","macroman","iso88591","shiftjis"],"description":"Character encoding for the output file.\n\n**Default:** `\"utf8\"`\n\nChange from the default only when the target system requires a specific encoding.","default":"utf8"},"delete":{"type":"boolean","description":"Whether to delete the source file after successful import.\n\n**Values**\n- `true`: Delete source file after processing\n- `false`: Keep source file\n\n**Default:** `false`","default":false},"compressionFormat":{"type":"string","enum":["gzip","zip"],"description":"Compression format for the output file.\n\n**When to use**\n- Large files that benefit from compression\n- When target system expects compressed files"},"backupPath":{"type":"string","description":"Path where backup copies of files should be stored.\n\n**Examples**\n- `\"backup/\"` - Relative backup folder\n- `\"/archive/2024/\"` - Absolute backup path"},"purgeInternalBackup":{"type":"boolean","description":"Whether to purge internal backup copies after successful processing.\n\n**Default:** `false`","default":false},"batchSize":{"type":"integer","description":"Number of records to include per batch/file when processing large datasets.\n\n**When to use**\n- Large imports that need to be split into multiple files\n- When target system has file size limitations\n\n**Note**\n- Offered when records are written one file per page (`aggregation.type: page` with\n  `skipAggregation: true`)"},"encrypt":{"type":"boolean","description":"Whether to encrypt the output file.\n\n**Values**\n- `true`: Encrypt the file (requires PGP configuration)\n- `false`: No encryption\n\n**Default:** `false`","default":false},"csv":{"type":"object","description":"CSV-specific configuration. Only used when `type` is `\"csv\"`.","properties":{"rowDelimiter":{"type":"string","description":"Character(s) used to separate rows. Default is newline.","default":"\n"},"columnDelimiter":{"type":"string","description":"Character(s) used to separate columns. Default is comma.","default":","},"includeHeader":{"type":"boolean","description":"When true, includes a header row with column names in the output.","default":true},"wrapWithQuotes":{"type":"boolean","description":"When true, wraps field values in quotes.","default":false},"replaceTabWithSpace":{"type":"boolean","description":"Replace tab characters with spaces.","default":false},"replaceNewlineWithSpace":{"type":"boolean","description":"Replace newline characters with spaces within fields.","default":false},"truncateLastRowDelimiter":{"type":"boolean","description":"Remove trailing row delimiter from the file.","default":false}}},"json":{"type":"object","description":"JSON-specific configuration. Only used when `type` is `\"json\"`.","properties":{"resourcePath":{"type":"string","description":"JSONPath expression to locate records within the JSON structure."}}},"xml":{"type":"object","description":"XML-specific configuration. Only used when `type` is `\"xml\"`.","properties":{"resourcePath":{"type":"string","description":"XPath expression to locate records within the XML structure."}}},"fileDefinition":{"type":"object","description":"Configuration settings for parsing files using a predefined file definition. This object enables processing of complex, non-standard, or proprietary file formats that require specialized parsing logic beyond what the standard parsers (CSV, JSON, XML, etc.) can handle.\n\n**When to use**\n\nConfigure this object when the `type` field is set to \"filedefinition\". This approach is required for properly handling:\n- Legacy or proprietary file formats with complex structures\n- Fixed-width text files where field positions are defined by character positions\n- Electronic Data Interchange (EDI) documents (X12, EDIFACT, etc.)\n- Multi-record type files where different lines have different formats\n- Files requiring complex preprocessing or custom parsing logic\n\n**File definition characteristics**\n\n- **Custom Parsing Rules**: Applies predefined parsing logic to complex file formats\n- **Reusable Configurations**: References externally defined parsing rules that can be reused\n- **Complex Format Support**: Handles formats that standard parsers cannot process\n- **Specialized Processing**: Often used for industry-specific or legacy formats\n\n**Implementation strategy for ai agents**\n\n1. **Format Analysis**:\n    - Determine if the file format is standard (CSV, JSON, XML) or requires custom parsing\n    - Check if the format follows industry standards like EDI, SWIFT, or fixed-width\n    - Assess if there are multiple record types within the same file\n    - Identify if specialized logic is needed to interpret the file structure\n\n2. **File Definition Selection**:\n    - Verify that a suitable file definition has already been created in the system\n    - Check if existing file definitions match the format requirements\n    - Confirm the file definition ID from system administrators if needed\n    - Ensure the file definition is compatible with the export's needs\n","properties":{"_fileDefinitionId":{"type":"string","format":"objectId","description":"Reference to the file definition resource."}}},"aggregation":{"type":"object","description":"Controls how a flow run's records are batched into output files — the transfer form's\n\"How many files would you like to generate?\" setting. The legacy `skipAggregation` flag\nmirrors `type` and is maintained by the platform.","properties":{"type":{"type":"string","enum":["all","page","key"],"description":"Which batches of a run's records become separate output files. EDI file definitions\n(`file.type: filedefinition`) are written per page or per key, never per run; `key`\nis accepted only for them and only when the account has B2B Manager. The grouping\nkey and the line-item loop it merges come from the EDI file definition's document\ntype, so there is no separate key field to configure."}}},"lookups":{"type":"array","description":"Named value-substitution maps available to this import's field mappings; each lookup\ntranslates source values to target values, with an optional default for unmatched values.\nReferenced by name from mapping rules.","items":{"type":"object","properties":{"name":{"type":"string","description":"Identifier used to reference this lookup from mapping rules."},"map":{"type":"object","description":"Source-value to target-value pairs applied during mapping.","additionalProperties":{"type":"string"}},"default":{"type":["string","null"],"description":"Value substituted when a source value is not found in map; empty string when omitted. May be stored as null (no default)."},"allowFailures":{"type":"boolean","description":"When true, a value missing from map does not fail the record and the default (or original value) is used."}}}},"pgp":{"type":"object","description":"PGP encryption settings applied when encrypt is true; selects the symmetric cipher and the\nsigning hash. The connection must hold the recipient's public key.","properties":{"symmetricKeyAlgorithm":{"type":"string","enum":["twofish","cast5","3des","aes128","aes192","aes256"],"default":"aes256","description":"Symmetric cipher used to encrypt the file body; aes256 unless the recipient requires another."},"hashAlgorithm":{"type":"string","enum":["sha256","sha384","sha512","sha224"],"description":"Hash algorithm used when signing the encrypted file; set only when the recipient requires a specific one."}}},"skipRename":{"type":"boolean","description":"When true, the import writes directly to the final file name instead of writing to a\ntemporary name and renaming on completion. Use only when the destination does not support\natomic rename; the default (false) uses the safe write-then-rename behavior."},"directory":{"type":"object","required":["pathMode"],"description":"Structured destination location for cloud file-provider imports (Google Drive shared\ndrives, Box, Dropbox), replacing the flat path used by classic FTP/S3 destinations. Select\nthe location by folder ID or by a path relative to a configured storage root via pathMode.\nFor these cloud providers, directoryId mode addresses the folder by id alone — the flat\ndestination-path field is not consulted at runtime.","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 destination directory (e.g. a Google Drive folder ID);\nset when pathMode is directoryId. This is the file provider's own ID, not a Celigo resource ID."},"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."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root the relative path resolves against; set when\npathMode is relativePath. The provider's own ID, not a Celigo resource ID."},"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 location for backup copies on cloud file providers — the file-provider\ncounterpart 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."}}}}},"FileSystem":{"type":"object","description":"Defines where files are written to 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 where generated files are written; the agent's OS\naccount must have write permission on it. Accepts a local OS path or a UNC network share,\nand supports handlebars templates for dynamic folders."}}},"AiAgentConfig":{"type":"object","description":"AI Agent configuration used by both AiAgentImport and GuardrailImport (ai_agent type).\n\nConfigures which AI provider and model to use, along with instructions, parameter\ntuning, output format, and available tools. Providers come in two families with\ntwo configuration formats:\n\n- **Built-in providers** — each has its own configuration block.\n  - **openai**: OpenAI models (GPT-4.1, GPT-5, etc.). Configure via the `openai` object.\n  - **gemini**: Google Gemini models via the LiteLLM proxy. Configure via `litellm` with overrides in `litellm._overrides.gemini`.\n  - **anthropic**: Anthropic Claude models via the LiteLLM proxy. Configure via `litellm` with overrides in `litellm._overrides.anthropic`.\n- **Catalog providers** — `mistral`, `xai`, `huggingface`, `deepseek`, `cohere`, `groq`. Added\n  as catalog data rather than platform code, they share one generic flat format:\n  `model`, `modelOptions`, `instructions`, `output`, and the `tools` / `prompts` /\n  `resources` arrays directly on this object.\n\nA `_connectionId` on the parent import selects bring-your-own-key (BYOK)\ncredentials. Without one, built-in providers run on platform-managed\ncredentials; catalog providers have no platform-managed credentials, so they\nsave without a connection but cannot run until one is attached.\n","required":["provider"],"properties":{"provider":{"type":"string","enum":["openai","gemini","anthropic","mistral","xai","huggingface","deepseek","cohere","groq"],"description":"AI provider to use."},"model":{"type":"string","description":"Model identifier for a catalog provider (generic flat format) — for example\n`grok-4.6`. Built-in providers set the model inside their own block\n(`openai.model`, `litellm.model`) instead."},"modelOptions":{"type":"object","description":"Model tuning for a catalog provider (generic flat format). Which keys, values, and\nranges are accepted is defined per catalog model and validated on save when the\nmodel is a catalog entry.","properties":{"maxOutputTokens":{"type":"number","description":"Maximum number of tokens the model may generate."},"temperature":{"type":"number","description":"Sampling temperature."},"topP":{"type":"number","description":"Nucleus sampling threshold."},"reasoning_effort":{"type":"string","enum":["low","medium","high","xhigh"],"description":"Reasoning depth for models that expose it."}}},"instructions":{"type":"string","description":"System prompt for a catalog provider (generic flat format). Built-in providers\nset it inside their own block (`openai.instructions`,\n`litellm._overrides.anthropic.systemInstruction`)."},"output":{"type":"object","description":"Output format for a catalog provider (generic flat format). Built-in providers\nconfigure it inside their own block (`openai.output`, `litellm.responseFormat`).","properties":{"format":{"type":"object","description":"Controls the structure of the model's output.","properties":{"type":{"type":"string","enum":["text","json_schema"],"description":"Output type."},"name":{"type":"string","description":"Name of the JSON schema, for `json_schema` output."},"strict":{"type":"boolean","description":"When true, the model must conform exactly to `jsonSchema`."},"jsonSchema":{"type":"object","additionalProperties":true,"description":"JSON Schema the structured output must conform to, for `json_schema` output."}}},"verbose":{"type":"string","description":"Level of detail in the model's response, for models that expose it."}}},"tools":{"type":"array","description":"Tools available to a catalog-provider agent (generic flat format). Entries mirror\nthe built-in providers' tool entries: a Celigo Tool by `_toolId`, an MCP server by\n`_mcpConnectionId`, or a vendor web-search tool.","items":{"type":"object","properties":{"type":{"type":"string","enum":["tool","mcp","web_search"],"description":"Type of tool entry."},"tool":{"type":"object","description":"Reference to a Celigo Tool resource, used when type is \"tool\".","properties":{"_toolId":{"type":"string","format":"objectId","description":"The Celigo Tool to call."}}},"mcp":{"type":"object","description":"MCP server connection, used when type is \"mcp\".","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if omitted). Each entry\nis either a plain tool name or an object carrying display metadata.","items":{"type":["string","object"]}}}},"config":{"type":"object","additionalProperties":true,"description":"Tool-specific options for vendor-native tools (for example web-search settings)."}}}},"prompts":{"type":"array","description":"MCP prompt entries available to a catalog-provider agent (generic flat format).\nEach entry references one MCP connection and the prompt names allowed from it.","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server that exposes the prompts."},"allowedPrompts":{"type":"array","description":"Prompt names the agent may fetch from the server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"},"openai":{"type":"object","description":"OpenAI-specific configuration. Used when `provider` is \"openai\".\n","required":["model","instructions"],"properties":{"instructions":{"type":"string","maxLength":1000000,"description":"System prompt that defines the AI agent's behavior, goals, and constraints.\n"},"model":{"type":"string","description":"OpenAI model identifier. Open string (not an enum) — model names change frequently."},"reasoning":{"type":"object","description":"Controls depth of reasoning for complex tasks.","properties":{"effort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh"],"description":"How much reasoning effort the model should invest"},"summary":{"type":"string","enum":["concise","auto","detailed"],"description":"Level of detail in reasoning summaries"}}},"temperature":{"type":"number","minimum":0,"maximum":2,"description":"Sampling temperature. Higher values (e.g. 1.5) produce more creative output,\nlower values (e.g. 0.2) produce more focused and deterministic output.\n"},"topP":{"type":"number","minimum":0.1,"maximum":1,"description":"Nucleus sampling parameter"},"topLogprobs":{"type":"number","minimum":0,"maximum":20,"description":"Number of most likely tokens to return log probabilities for at each output position."},"maxOutputTokens":{"type":"number","minimum":100,"maximum":128000,"default":5000,"description":"Maximum number of tokens in the model's response (server default observed live on create)"},"serviceTier":{"type":"string","enum":["auto","default","priority"],"default":"default","description":"OpenAI service tier. \"priority\" provides higher rate limits and\nlower latency at increased cost."},"output":{"type":"object","description":"Output format configuration","properties":{"format":{"type":"object","description":"Controls the structure of the model's output.\n","properties":{"type":{"type":"string","enum":["text","json_schema","blob"],"default":"text","description":"Output format type."},"schemaMode":{"type":"string","enum":["manual","json"],"description":"How the structured-output schema was authored in the UI.\nEditor state only — it does not change how `jsonSchema` is\nsent to the provider."},"name":{"type":"string","description":"Name for the output format (used with json_schema)"},"strict":{"type":"boolean","default":false,"description":"When true, enforces strict schema validation on output."},"jsonSchema":{"type":"object","description":"JSON Schema for structured output. Required when `format.type` is \"json_schema\".\n","properties":{"type":{"type":"string","description":"Root JSON Schema type of the structured output; use \"object\" for record-shaped results.","enum":["object","array","string","number","integer","boolean"]},"properties":{"type":"object","additionalProperties":true,"description":"JSON Schema definitions for each field the structured output may contain."},"required":{"type":"array","description":"Property names the model must include in the structured output.","items":{"type":"string"}},"additionalproperties":{"type":"boolean","description":"When true, the structured output may include properties beyond those defined in `properties`."}}}},"if":{"properties":{"type":{"const":"json_schema"}},"required":["type"]},"then":{"required":["name","jsonSchema"]}},"verbose":{"type":"string","enum":["low","medium","high"],"default":"medium","description":"Level of detail in the model's response"}}},"tools":{"type":"array","description":"Tools available to the AI agent during processing.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["web_search","mcp","image_generation","tool"],"description":"Type of tool."},"webSearch":{"type":"object","description":"Web search configuration (empty object to enable)"},"imageGeneration":{"type":"object","description":"Image generation configuration","properties":{"background":{"type":"string","description":"Controls whether generated images have a transparent or opaque background; use transparent only with output formats that support it (png, webp).","enum":["transparent","opaque"]},"quality":{"type":"string","description":"Rendering quality of generated images, trading detail for generation speed and file size.","enum":["low","medium","high"]},"size":{"type":"string","description":"Pixel dimensions of generated images; choose square, portrait, or landscape to match the intended use.","enum":["1024x1024","1024x1536","1536x1024"]},"outputFormat":{"type":"string","description":"File format of generated images; use png or webp when transparency is needed.","enum":["png","webp","jpeg"]}}},"mcp":{"type":"object","description":"MCP server tool configuration","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server"},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if\nomitted). Each entry is either a plain tool name (legacy\nform) or an object carrying display metadata.","items":{"type":["string","object"],"properties":{"name":{"type":"string","maxLength":256,"description":"Tool name as exposed by the MCP server."},"title":{"type":"string","maxLength":300,"description":"Display title shown for the tool."},"description":{"type":"string","maxLength":1000,"description":"Display description shown for the tool."}},"required":["name"]}},"allowedPrompts":{"type":"array","description":"Specific prompts to allow from the MCP server (used for MCP prompt entries; all if omitted).","items":{"type":"string"}}}},"tool":{"type":"object","description":"Reference to a Celigo Tool resource.\n","properties":{"_toolId":{"type":"string","format":"objectId","description":"Reference to the Tool resource"},"overrides":{"type":"object","description":"Per-agent overrides for the tool's internal resources","properties":{"connections":{"type":"array","description":"Remaps the tool's abstract connections for this agent. Each entry pairs\nthe tool's abstract connection placeholder (`_abstractId`) with the\nconcrete connection (`_id`) to use for this agent; entries without\n`_id` keep the tool's own default connection.\n","items":{"type":["object","null"],"required":["_abstractId"],"properties":{"_abstractId":{"type":"string","format":"objectId","description":"The tool's abstract connection placeholder being overridden."},"_id":{"type":"string","format":"objectId","description":"Concrete connection to use in place of the abstract placeholder."}}}}}}}}}}},"prompts":{"type":"array","description":"MCP prompt entries available to the agent. Each item references one MCP connection\nand the prompt names allowed from it. Configured alongside `tools` in the form but\nstored separately; an entry's `allowedPrompts` is what distinguishes a prompt entry\nfrom an MCP tool entry (which carries `allowedTools`).\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","description":"MCP server prompt configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedPrompts":{"type":"array","description":"Prompt names to allow from the MCP server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"}}},"litellm":{"type":"object","description":"LiteLLM proxy configuration. Used when `provider` is \"gemini\" or \"anthropic\".\n\nLiteLLM provides a unified interface to multiple AI providers. Gemini-specific\nsettings are in `_overrides.gemini`; Claude-specific settings are in\n`_overrides.anthropic`.\n\n`model` is required when litellm is the active provider path.\n","properties":{"model":{"type":"string","description":"LiteLLM model identifier. For Gemini, models are stored without the `gemini/`\nprefix; for Anthropic, use the Claude model id (e.g. `claude-sonnet-4-6`)."},"temperature":{"type":"number","minimum":0,"maximum":2,"description":"Sampling temperature"},"maxCompletionTokens":{"type":"number","minimum":100,"maximum":128000,"default":5000,"description":"Maximum number of tokens in the response"},"topP":{"type":"number","minimum":0.1,"maximum":1,"description":"Nucleus sampling parameter"},"seed":{"type":"number","description":"Random seed for reproducible outputs"},"responseFormat":{"type":"object","description":"Output format configuration","properties":{"type":{"type":"string","description":"Output format type.","enum":["text","json_schema","blob"],"default":"text"},"schemaMode":{"type":"string","enum":["manual","json"],"description":"How the structured-output schema was authored in the UI. Editor\nstate only — it does not change how `jsonSchema` is sent to the\nprovider."},"name":{"type":"string","description":"Name for the output format (used with json_schema)."},"strict":{"type":"boolean","description":"When true, enforces strict schema validation on output.","default":false},"jsonSchema":{"type":"object","description":"JSON Schema for structured output. Required when `responseFormat.type` is \"json_schema\".","properties":{"type":{"type":"string","description":"Root JSON Schema type of the structured output; use \"object\" for record-shaped results.","enum":["object","array","string","number","integer","boolean"]},"properties":{"type":"object","additionalProperties":true,"description":"JSON Schema definitions for each field the structured output may contain."},"required":{"type":"array","description":"Property names the model must include in the structured output.","items":{"type":"string"}},"additionalProperties":{"type":"boolean","description":"When true, the structured output may include properties beyond those defined in `properties`."}}}},"if":{"properties":{"type":{"const":"json_schema"}},"required":["type"]},"then":{"required":["name","jsonSchema"]}},"_overrides":{"type":"object","description":"Provider-specific overrides","properties":{"gemini":{"type":"object","description":"Gemini-specific configuration overrides.\n","required":["systemInstruction"],"properties":{"systemInstruction":{"type":"string","maxLength":1000000,"description":"System instruction for Gemini models. Equivalent to OpenAI's `instructions`.\nMaximum 1,000,000 characters.\n"},"tools":{"type":"array","description":"Gemini-specific tools","items":{"type":"object","properties":{"type":{"type":"string","enum":["googleSearch","urlContext","fileSearch","mcp","tool"],"description":"Type of Gemini tool."},"googleSearch":{"type":"object","description":"Google Search configuration (empty object to enable)"},"urlContext":{"type":"object","description":"URL context configuration (empty object to enable)"},"fileSearch":{"type":"object","description":"File search configuration, used when type is \"fileSearch\".","properties":{"fileSearchStoreNames":{"type":"array","description":"Names of the file search stores the model can query.","items":{"type":"string"}}}},"mcp":{"type":"object","description":"MCP server tool configuration, used when type is \"mcp\".","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if\nomitted). Each entry is either a plain tool name\n(legacy form) or an object carrying display metadata\n— same contract as the OpenAI `allowedTools`.","items":{"type":["string","object"],"properties":{"name":{"type":"string","maxLength":256,"description":"Tool name as exposed by the MCP server."},"title":{"type":"string","maxLength":300,"description":"Display title shown for the tool."},"description":{"type":"string","maxLength":1000,"description":"Display description shown for the tool."}},"required":["name"]}},"allowedPrompts":{"type":"array","description":"Specific prompts to allow from the MCP server (used for MCP prompt entries; all if omitted).","items":{"type":"string"}}}},"tool":{"type":"object","description":"Reference to a Celigo Tool resource, used when type is \"tool\".","properties":{"_toolId":{"type":"string","format":"objectId","description":"Reference to the Tool resource."},"overrides":{"type":"object","description":"Per-agent overrides for the tool's internal resources.","properties":{"connections":{"type":"array","description":"Remaps the tool's abstract connections for this agent. Each entry\npairs the tool's abstract connection placeholder (`_abstractId`)\nwith the concrete connection (`_id`) to use for this agent;\nentries without `_id` keep the tool's own default connection.\n","items":{"type":["object","null"],"required":["_abstractId"],"properties":{"_abstractId":{"type":"string","format":"objectId","description":"The tool's abstract connection placeholder being overridden."},"_id":{"type":"string","format":"objectId","description":"Concrete connection to use in place of the abstract placeholder."}}}}}}}}}}},"prompts":{"type":"array","description":"MCP prompt entries available to the Gemini agent. Each item references one\nMCP connection and the prompt names allowed from it. The presence of\n`allowedPrompts` distinguishes a prompt entry from an MCP tool entry.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","description":"MCP server prompt configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedPrompts":{"type":"array","description":"Prompt names to allow from the MCP server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"},"responseModalities":{"type":"array","description":"Response output modalities","items":{"type":"string","enum":["text","image"]},"default":["text"]},"topK":{"type":"number","description":"Top-K sampling parameter for Gemini"},"thinkingConfig":{"type":"object","description":"Controls Gemini's extended thinking capabilities","properties":{"includeThoughts":{"type":"boolean","description":"When true, includes the model's thinking steps in the response."},"thinkingBudget":{"type":"number","minimum":100,"maximum":4000,"description":"Maximum tokens allocated for thinking"},"thinkingLevel":{"type":"string","description":"Controls how much thinking effort the model applies; use higher levels for complex, multi-step tasks at the cost of latency and tokens.","enum":["minimal","low","medium","high"]}}},"imageConfig":{"type":"object","description":"Gemini image generation configuration","properties":{"aspectRatio":{"type":"string","description":"Aspect ratio of generated images; choose a ratio matching the intended display format.","enum":["1:1","2:3","3:2","3:4","4:3","4:5","5:4","9:16","16:9","21:9"]},"imageSize":{"type":"string","description":"Output resolution of generated images; higher resolutions increase detail and file size.","enum":["1K","2K","4K"]}}},"mediaResolution":{"type":"string","enum":["low","medium","high"],"description":"Resolution for media inputs (images, video)"}}},"anthropic":{"type":"object","description":"Claude-specific configuration overrides. Used when `provider` is \"anthropic\".\n","required":["systemInstruction"],"properties":{"systemInstruction":{"type":"string","maxLength":1000000,"description":"System instruction for Claude models. Equivalent to OpenAI's `instructions`.\nMaximum 1,000,000 characters.\n"},"topK":{"type":"number","minimum":0,"description":"Top-K sampling parameter. Deprecated on Claude models released after Claude Opus\n4.6, which reject any value at runtime; set it only on older models."},"thinkingConfig":{"type":"object","description":"Controls Claude's extended thinking capabilities.","properties":{"type":{"type":"string","enum":["enabled","disabled","adaptive"],"default":"disabled","description":"Extended thinking mode."},"budgetTokens":{"type":"number","minimum":1024,"description":"Maximum tokens allocated for thinking. Required when `type` is \"enabled\" —\nomitting it fails the save with 422 `invalid_thinking_config`."},"display":{"type":"string","enum":["summarized","omitted"],"default":"summarized","description":"How thinking output is surfaced in the response."},"effort":{"type":"string","enum":["low","medium","high","xhigh","max"],"description":"How much thinking effort the model applies; use with `type` \"adaptive\". Higher\nvalues (`xhigh`, `max`) may be gated to specific Claude models by the provider."}},"if":{"properties":{"type":{"const":"enabled"}},"required":["type"]},"then":{"required":["budgetTokens"]}},"serviceTier":{"type":"string","enum":["auto","standard_only"],"default":"auto","description":"Anthropic service tier for the request."},"tools":{"type":"array","description":"Claude-specific tools.","items":{"type":"object","properties":{"type":{"type":"string","enum":["tool","mcp","webSearch"],"description":"Type of Claude tool."},"tool":{"type":"object","description":"Reference to a Celigo Tool resource, used when type is \"tool\".","properties":{"_toolId":{"type":"string","format":"objectId","description":"Reference to the Tool resource."},"overrides":{"type":"object","description":"Per-agent overrides for the tool's internal resources.","properties":{"connections":{"type":"array","description":"Remaps the tool's abstract connections for this agent. Each entry\npairs the tool's abstract connection placeholder (`_abstractId`)\nwith the concrete connection (`_id`) to use for this agent;\nentries without `_id` keep the tool's own default connection.\n","items":{"type":["object","null"],"required":["_abstractId"],"properties":{"_abstractId":{"type":"string","format":"objectId","description":"The tool's abstract connection placeholder being overridden."},"_id":{"type":"string","format":"objectId","description":"Concrete connection to use in place of the abstract placeholder."}}}}}}}},"mcp":{"type":"object","description":"MCP server tool configuration, used when type is \"mcp\".","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if omitted).","items":{"type":"string"}}}},"webSearch":{"type":"object","description":"Web search configuration, used when type is \"webSearch\".","properties":{"version":{"type":"string","pattern":"^\\d{8}$","description":"Anthropic web search tool version (YYYYMMDD). Selects the tool version sent\non the wire; unsupported versions surface as an Anthropic 400."},"allowedDomains":{"type":"array","description":"Domains the search may return results from. Mutually exclusive with `blockedDomains`.","items":{"type":"string"}},"blockedDomains":{"type":"array","description":"Domains to exclude from search results. Mutually exclusive with `allowedDomains`.","items":{"type":"string"}},"userLocation":{"type":"object","description":"Approximate user location used to localize search results. When present, at\nleast one of `city`, `country`, `region`, or `timezone` must be set.","properties":{"type":{"type":"string","enum":["approximate"],"description":"Location type. Always \"approximate\"."},"city":{"type":"string","maxLength":256,"description":"City name for localizing search results."},"country":{"type":"string","maxLength":8,"description":"ISO 3166-1 alpha-2 country code for localizing search results."},"region":{"type":"string","maxLength":256,"description":"Region or state for localizing search results."},"timezone":{"type":"string","maxLength":64,"description":"IANA timezone for localizing search results."}}}}}}}},"prompts":{"type":"array","description":"MCP prompt entries available to the Claude agent. Each item references one MCP\nconnection and the prompt names allowed from it. The presence of `allowedPrompts`\ndistinguishes a prompt entry from an MCP tool entry.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","description":"MCP server prompt configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedPrompts":{"type":"array","description":"Prompt names to allow from the MCP server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"}}}}}}}},"if":{"properties":{"provider":{"const":"openai"}},"required":["provider"]},"then":{"required":["openai"],"properties":{"openai":{"required":["model","instructions"]}}},"else":{"if":{"properties":{"provider":{"enum":["gemini","anthropic"]}},"required":["provider"]},"then":{"required":["litellm"],"properties":{"litellm":{"required":["model"]}}},"else":{"if":{"properties":{"provider":{"enum":["mistral","xai","huggingface","deepseek","cohere","groq"]}},"required":["provider"]},"then":{"required":["model"]}}}},"McpResources":{"type":"array","description":"Governed MCP resources — read-only reference content (policies, schemas, documentation)\npulled from connected MCP servers and made available to the agent as a consistent source\nof truth. Each entry references one MCP connection and the specific resources allowed from it.","items":{"type":"object","required":["type","mcp"],"properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of resource entry. Always \"mcp\"."},"mcp":{"type":"object","required":["_mcpConnectionId","allowedResources"],"description":"MCP server resource configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server that exposes the resources."},"allowedResources":{"type":"array","minItems":1,"description":"Resources to expose to the agent from the MCP server. Must contain at least one\nentry; each entry identifies one resource by name and URI.","items":{"type":"object","required":["name","uri"],"properties":{"name":{"type":"string","description":"Display name of the MCP resource."},"uri":{"type":"string","description":"URI that identifies the resource on the MCP server."}}}}}}}}},"GuardrailConfig":{"type":"object","description":"Configuration for GuardrailImport adaptor type.\n\nGuardrails evaluate data flowing through integrations for safety and\ncompliance. The `type` field selects which check to apply, and the\ncorresponding sub-object (`aiAgent`, `pii`, or `moderation`) provides\nthe configuration.\n\nA `_connectionId` on the parent import is only needed for BYOK\n`ai_agent` guardrails. In responses the server echoes the active type's\nsub-object and applies the `confidenceThreshold` default; it also\nreturns inactive sibling sub-objects (e.g. `moderation: {categories: []}`\non a `pii` guardrail, or a populated `pii` left over from a type switch),\nbut only the active type's sub-object is meaningful. Legacy documents\nmay carry a server-written default `aiAgent` stub on `pii`/`moderation`\nguardrails; current servers strip the inactive `aiAgent` on write.","properties":{"type":{"type":"string","enum":["ai_agent","pii","moderation"],"description":"The type of guardrail to apply. Each type requires its corresponding\nsub-configuration object (`aiAgent`, `pii`, or `moderation`)."},"confidenceThreshold":{"type":"number","minimum":0,"maximum":1,"default":0.7,"description":"Confidence threshold (0 to 1). Detections below this threshold are\nignored. Lower values catch more issues but increase false positives."},"aiAgent":{"type":"object","description":"AI agent check configuration; set when `type` is `ai_agent`. On\n`pii`/`moderation` guardrails a legacy server-written stub may\nappear here — it is inert, and current servers strip it on write."},"pii":{"type":"object","required":["entities"],"description":"PII detection configuration. Required when `type` is `pii`.","properties":{"entities":{"type":"array","description":"PII entity types to detect. When `type` is `pii`, at least one\nentry is required; the inactive sibling on other guardrail types\nmay be served with an empty list.","items":{"type":"string","enum":["credit_card_number","card_security_code_cvv_cvc","cryptocurrency_wallet_address","date_and_time","email_address","iban_code","bic_swift_bank_identifier_code","ip_address","location","medical_license_number","national_registration_number","persons_name","phone_number","url","us_bank_account_number","us_drivers_license","us_itin","us_passport_number","us_social_security_number","uk_nhs_number","uk_national_insurance_number","spanish_nif","spanish_nie","italian_fiscal_code","italian_drivers_license","italian_vat_code","italian_passport","italian_identity_card","polish_pesel","finnish_personal_identity_code","singapore_nric_fin","singapore_uen","australian_abn","australian_acn","australian_tfn","australian_medicare","indian_pan","indian_aadhaar","indian_vehicle_registration","indian_voter_id","indian_passport","korean_resident_registration_number"]}},"mask":{"type":"boolean","default":false,"description":"When true, detected PII is replaced with masked values.\nWhen false, PII is flagged without modification."}}},"moderation":{"type":"object","required":["categories"],"description":"Content moderation configuration. Required when `type` is `moderation`.","properties":{"categories":{"type":"array","description":"Content moderation categories to check. When `type` is\n`moderation`, at least one entry is required; the inactive\nsibling on other guardrail types may be served with an empty\nlist.","items":{"type":"string","enum":["sexual","sexual_minors","hate","hate_threatening","harassment","harassment_threatening","self_harm","self_harm_intent","self_harm_instructions","violence","violence_graphic","illicit","illicit_violent"]}}}}},"required":["type","confidenceThreshold"],"if":{"required":["type"],"properties":{"type":{"const":"pii"}}},"then":{"required":["pii"],"properties":{"pii":{"required":["entities"],"properties":{"entities":{"minItems":1}}}}},"else":{"if":{"required":["type"],"properties":{"type":{"const":"moderation"}}},"then":{"required":["moderation"],"properties":{"moderation":{"required":["categories"],"properties":{"categories":{"minItems":1}}}}},"else":{"required":["aiAgent"],"properties":{"aiAgent":{"$ref":"#/components/schemas/AiAgentConfig"}}}}},"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"},"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\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**: Using wildcards like `*` and `?`\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- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"isempty\"`: Field is empty or null (is empty)\n- `\"isnotempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches specified pattern (matches)\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- Returns the raw field value which may need type conversion\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  - `export.settingName`: Access export-level settings\n  - `import.settingName`: Access import-level settings\n  - `integration.settingName`: Access integration-level settings\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\nType conversion operators are often necessary when comparing extracted field values against literals or when the field type doesn't match the comparison operator's expected type. 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    \"extract\",\n    \"status\"\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"extract\",\n    \"filename\"\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\n```json\n[\n  \"greaterthan\",\n  [\n    \"extract\",\n    \"orderDate\"\n  ],\n  \"2023-01-01T00:00:00Z\"\n]\n```\n\nExample 5: Category contains any of [\"Urgent\", \"High Priority\"]\n```json\n[\n  \"anyof\",\n  [\n    \"extract\",\n    \"category\"\n  ],\n  [\"Urgent\", \"High Priority\"]\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 that exists in the system.\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"}}}}},"Hook":{"type":"object","description":"A single lifecycle hook — a JavaScript function invoked from a script (or hosted on a\nstack) at a fixed point in an import or AI-agent run. Shared by every hook slot; the\nslot's own description says when in the lifecycle it fires.","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","null"],"description":"Static parameters passed to the hook function at runtime, letting one script be\nreused with different settings. Null when the hook carries no static parameters."}}},"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.\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"}}}}},"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, arrays of objects, arrays of arrays of objects) are ALL\n   handled through this recursive pattern\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","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- For arrays within arrays: Leave EMPTY for the inner array mappings, as they don't have field names\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, empty 'generate' fields almost always indicate inner array structures\nwithin a parent array.\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, attempts conversion from strings\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. REQUIRES child mappings in the 'mappings' array\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- `arrayarray`: Array of arrays (for matrix/table structures)\n\nArray dataTypes can be populated two ways: pass a source array through\nunchanged via `extract` alone (when the source is already an array of\nthe right shape), or construct/iterate via `buildArrayHelper`.\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` is one option (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","arrayarray"]},"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\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 `object`, `objectarray`, and primitive-array dataTypes as\n  well as scalars — the rendered template output must parse into the\n  declared shape\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- When both extract and mappings are defined for objects, extract is applied first\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 empty (the UI's \"Do nothing\" action).\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.\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"},"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, date)\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 REQUIRED mechanism for ALL array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.)\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**For Arrays of Arrays (arrayarray)**\n- Similar to objectarray, but inner arrays have empty 'generate' fields\n- Used for matrix/table structures\n```json\n{\n  \"generate\": \"matrix\",\n  \"dataType\": \"arrayarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.rows[*]\",  // For each row in the rows array\n      \"mappings\": [\n        {\n          \"dataType\": \"numberarray\",  // Note: No generate field for inner arrays\n          \"buildArrayHelper\": [\n            {\"extract\": \"$.rows.columns[*]\"}  // Notice: \"rows\" is singular in the composite object\n          ]\n        }\n      ]\n    }\n  ]\n}\n```\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"},"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":"Specifies the condition that must be met for an array element to be included.\n\n'extract_not_empty' only includes elements where the extract field returns a value.\n","enum":["extract_not_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"}}}}},"status":{"type":"string","description":"**Purpose**\nRequired on every mapping entry. Controls whether the mapping is applied.\n\n**Guidance**\nEmit `\"Active\"` for mappings that should run; `\"Draft\"` saves an\nin-progress mapping without the Active-only field validations. The\nAPI rejects a mapping missing this field\n(validation error: \"Mapping object must have status field present.\").\n","enum":["Active","Draft"]},"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**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":"Specifies the condition that triggers application of this mapping:\n- record_created: Apply only when creating a new record\n- record_updated: Apply only when updating an existing record\n- extract_not_empty: Apply only when the extract field returns a value\n","enum":["record_created","record_updated","extract_not_empty"]}}}},"required":["dataType"]}},"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- For arrays within arrays: Leave EMPTY for the inner array mappings, as they don't have field names\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, empty 'generate' fields almost always indicate inner array structures\nwithin a parent array.\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, attempts conversion from strings\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. REQUIRES child mappings in the 'mappings' array\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- `arrayarray`: Array of arrays (for matrix/table structures)\n\nArray dataTypes can be populated two ways: pass a source array through\nunchanged via `extract` alone (when the source is already an array of\nthe right shape), or construct/iterate via `buildArrayHelper`.\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` is one option (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","arrayarray"]},"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\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 `object`, `objectarray`, and primitive-array dataTypes as\n  well as scalars — the rendered template output must parse into the\n  declared shape\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- When both extract and mappings are defined for objects, extract is applied first\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 empty (the UI's \"Do nothing\" action).\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.\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"},"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, date)\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 REQUIRED mechanism for ALL array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.)\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**For Arrays of Arrays (arrayarray)**\n- Similar to objectarray, but inner arrays have empty 'generate' fields\n- Used for matrix/table structures\n```json\n{\n  \"generate\": \"matrix\",\n  \"dataType\": \"arrayarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.rows[*]\",  // For each row in the rows array\n      \"mappings\": [\n        {\n          \"dataType\": \"numberarray\",  // Note: No generate field for inner arrays\n          \"buildArrayHelper\": [\n            {\"extract\": \"$.rows.columns[*]\"}  // Notice: \"rows\" is singular in the composite object\n          ]\n        }\n      ]\n    }\n  ]\n}\n```\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"},"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":"Specifies the condition that must be met for an array element to be included.\n\n'extract_not_empty' only includes elements where the extract field returns a value.\n","enum":["extract_not_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"}}}}},"status":{"type":"string","description":"**Purpose**\nRequired on every mapping entry. Controls whether the mapping is applied.\n\n**Guidance**\nEmit `\"Active\"` for mappings that should run; `\"Draft\"` saves an\nin-progress mapping without the Active-only field validations. The\nAPI rejects a mapping missing this field\n(validation error: \"Mapping object must have status field present.\").\n","enum":["Active","Draft"]},"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**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":"Specifies the condition that triggers application of this mapping:\n- record_created: Apply only when creating a new record\n- record_updated: Apply only when updating an existing record\n- extract_not_empty: Apply only when the extract field returns a value\n","enum":["record_created","record_updated","extract_not_empty"]}}}},"required":["dataType"]},"MappingField":{"type":"object","description":"One Mapper 1.0 field-mapping entry.","properties":{"generate":{"type":"string","description":"Target field path to write on the destination record."},"extract":{"type":"string","description":"Source expression. Accepts:\n- JSONPath starting with `$.` (e.g. `$.customer.firstName`).\n- Handlebars template (contains `{{`) for concatenation /\n  formatting / conditional logic (e.g.\n  `{{record.firstName}} {{record.lastName}}`).\n- A literal string (any value not starting with `$.` and not\n  containing `{{`) — treated as a hardcoded value.\n\nOmit when using `hardCodedValue`.\n"},"hardCodedValue":{"type":["string","null"],"description":"Static value written to `generate` instead of extracting from the source record."},"dataType":{"type":"string","enum":["string","number","boolean","numberarray","stringarray","json"],"description":"Data type coercion applied to the mapped value."},"discardIfEmpty":{"type":"boolean","description":"When true, the field is omitted from the output when the extracted value is empty."},"immutable":{"type":"boolean","description":"When true, the mapped value cannot be overwritten by later mapping steps."},"lookupName":{"type":"string","description":"Name of an entry in the import's `lookups` array used to translate the value."},"default":{"type":["string","null"],"description":"Fallback value used when the extract yields no value."},"extractDateFormat":{"type":"string","description":"Date format of the source value, used to parse it before conversion."},"extractDateTimezone":{"type":"string","description":"Timezone applied when parsing the source date value."},"generateDateFormat":{"type":"string","description":"Date format applied to the value written to the destination."},"generateDateTimezone":{"type":"string","description":"Timezone applied when formatting the destination date value."},"conditional":{"type":"object","description":"Only apply this mapping entry when the given condition is satisfied.\n","properties":{"when":{"type":"string","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","expression"],"description":"Condition that gates whether this mapping entry is applied.\n`lookup_not_empty` / `lookup_empty` evaluate the lookup named by the\nsibling `lookupName`; `expression` evaluates the sibling `expression`.\n"},"lookupName":{"type":"string","description":"Lookup to evaluate for the `lookup_not_empty` / `lookup_empty`\nconditions.\n"},"expression":{"type":"string","description":"Expression evaluated when `when` is `expression`.\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.\nThis value typically matches the key in the fieldMap object.\n"},"name":{"type":"string","description":"Name of the field, used as the property name when generating the settings object\nfrom the submitted form data.\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.\n","default":false},"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 fields, specifies the placeholder and field name for the key input.\n"},"valueName":{"type":"string","description":"For keyvalue fields, specifies the placeholder and field name for the value input.\n"},"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.\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"]},"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.\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","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. This script must be accessible within the user's account\nand have appropriate permissions.\n"}}}}},"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"}}},"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},"Import":{"type":"object","required":["_id","name","adaptorType","apiIdentifier","createdAt","lastModified"],"description":"Import object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ImportBase"},{"$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 import."},"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the source resource this import was created from."},"_templateId":{"type":"string","format":"objectId","readOnly":true,"description":"Template this import was created from."},"draft":{"type":"boolean","readOnly":true,"description":"When true, this import 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 import expires."},"debugUntil":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp until which debug logging is enabled for this import."}}}]},"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":"Reference to the integration app that defines this resource.\n\nThis field is only populated for resources that are part of an integration app.\nIt contains the unique identifier (_id) of the integration app (connector)\nthat defines the structure, behavior, and templates for this resource.\n\nThe integration app is the published template that can be installed\nmultiple times across different accounts, with each installation creating\na separate integration instance (referenced by _integrationId).\n\nThis reference enables:\n- Identifying the source integration app for this resource\n- Determining which template version is being used\n- Linking to documentation, support, and marketplace information\n"}}},"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/imports":{"post":{"summary":"Create an import","description":"Creates a new import configuration that can be used to send data to applications\nor external destinations.\n","operationId":"createImport","tags":["Imports"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Request"}}}},"responses":{"201":{"description":"Import created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Import"}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
````

## Get an import

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

````json
{"openapi":"3.2.0","info":{"title":"Imports","version":"1.0.0"},"tags":[{"name":"Imports","description":"Imports deliver data to destination systems. An import receives pages of records from\nupstream flow steps, applies field mappings and transformations, and writes the results\nto the target application, database, or file destination — handling lookups, duplicate\navoidance, and per-record error reporting along the way. Each import uses one\n`adaptorType` that determines its connection compatibility and configuration object.\n\n## Import schema\n\n{% openapi-schemas spec=\"import\" schemas=\"Import\" 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":{"Import":{"type":"object","required":["_id","name","adaptorType","apiIdentifier","createdAt","lastModified"],"description":"Import object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ImportBase"},{"$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 import."},"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the source resource this import was created from."},"_templateId":{"type":"string","format":"objectId","readOnly":true,"description":"Template this import was created from."},"draft":{"type":"boolean","readOnly":true,"description":"When true, this import 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 import expires."},"debugUntil":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp until which debug logging is enabled for this import."}}}]},"ImportBase":{"type":"object","description":"Writable import fields shared by the request and response schemas.","properties":{"_connectionId":{"type":"string","format":"objectId","description":"Connection this import uses to reach the destination system. The connection's type must\nbe compatible with the import's `adaptorType` (e.g. an `HTTPImport` needs an `http`\nconnection; `NetSuiteDistributedImport` and `NetSuiteHTTPImport` both need a `netsuite`\nconnection). Server-required — POST without it fails with 422 \"Expected field:\n_connectionId to be present\" — except for the connection-less flavors (`ToolImport`,\n`AiAgentImport`, `GuardrailImport`), which the server creates without one."},"_integrationId":{"type":["string","null"],"format":"objectId","description":"Integration this import belongs to."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this import was created from, set when the import is part of an installed integration app."},"adaptorType":{"type":"string","description":"Selects the adaptor technology that executes this import, which determines the compatible\nconnection types and which adaptor-specific configuration object must also be supplied\n(e.g. set `salesforce` when using `SalesforceImport`).","enum":["HTTPImport","FTPImport","AS2Import","S3Import","NetSuiteImport","NetSuiteDistributedImport","NetSuiteHTTPImport","SalesforceImport","JDBCImport","RDBMSImport","MongodbImport","DynamodbImport","WrapperImport","AiAgentImport","GuardrailImport","FileSystemImport","ToolImport","RESTImport"]},"nsDomainType":{"type":"string","enum":["suitetalk","restlet"],"description":"Selects which NetSuite REST host a `NetSuiteHTTPImport` 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 each\n`http.relativeURI` entry carries the complete path starting at `/services/rest/...` or\n`/app/site/hosting/restlet.nl`. Ignored on other adaptor types."},"externalId":{"type":["string","null"],"description":"External identifier for correlating the import with a record in another system."},"as2":{"$ref":"#/components/schemas/As2"},"dynamodb":{"$ref":"#/components/schemas/Dynamodb"},"http":{"$ref":"#/components/schemas/Http"},"ftp":{"$ref":"#/components/schemas/Ftp"},"jdbc":{"$ref":"#/components/schemas/Jdbc"},"mongodb":{"$ref":"#/components/schemas/Mongodb"},"netsuite":{"$ref":"#/components/schemas/NetSuite"},"netsuite_da":{"$ref":"#/components/schemas/NetsuiteDistributed"},"rdbms":{"$ref":"#/components/schemas/Rdbms"},"s3":{"$ref":"#/components/schemas/S3"},"wrapper":{"$ref":"#/components/schemas/Wrapper"},"salesforce":{"$ref":"#/components/schemas/Salesforce"},"tool":{"$ref":"#/components/schemas/Tool"},"file":{"$ref":"#/components/schemas/File"},"filesystem":{"$ref":"#/components/schemas/FileSystem"},"aiAgent":{"$ref":"#/components/schemas/AiAgentConfig"},"guardrail":{"$ref":"#/components/schemas/GuardrailConfig"},"name":{"type":"string","minLength":1,"maxLength":100,"description":"Display name for the import, shown in the flow builder, job history, and error logs.\nDescriptive, unique names indicating the destination system and purpose make large\naccounts easier to manage."},"description":{"type":["string","null"],"description":"Free-text summary of what the import writes and why. Shown in the UI and available to\nAI agents for context; has no effect on execution.","maxLength":5120},"unencrypted":{"type":"object","description":"Custom configuration values stored without encryption and returned in API responses."},"sampleData":{"type":["object","array","string"],"description":"Sample input record used to preview and build the import's mappings."},"distributed":{"type":"boolean","description":"When true, the import uses a distributed adaptor (such as `NetSuiteDistributedImport`)\nthat executes inside the target application rather than on Celigo's servers."},"maxAttempts":{"type":"number","description":"Maximum number of attempts made to deliver a record before it is marked as failed."},"ignoreExisting":{"type":"boolean","description":"When true, records that already exist in the destination system are silently skipped\ninstead of being created or updated — used for create-only operations that must avoid\nduplicates. Existing records are identified by the import's lookup configuration or by\na populated `ignoreExtract` field on the incoming record."},"ignoreMissing":{"type":"boolean","description":"When true, records that do not already exist in the destination system are silently\nskipped instead of producing errors — used for update-only operations."},"idLockTemplate":{"type":["string","null"],"description":"Handlebars template that generates a lock key for each record so records resolving to\nthe same key are not submitted concurrently, preventing duplicate or conflicting writes\nto the same target record."},"dataURITemplate":{"type":["string","null"],"description":"Handlebars template that builds a link back to each record in the destination\napplication's UI. The resolved URL is stored with error records in job history so users\ncan jump straight to the record."},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"blobKeyPath":{"type":"string","description":"Path in the input record that holds the blob key identifying the file content to import.\nAt send time the platform follows this path, retrieves the referenced file from integrator.io\nstorage, and streams its bytes into the outgoing request."},"blob":{"type":"boolean","description":"When true, this import transfers raw file content (blobs) to the destination rather than structured records."},"assistant":{"type":"string","description":"Identifier for the connector assistant used to configure this import."},"deleteAfterImport":{"type":"boolean","description":"When true, the source file is deleted after it is successfully imported."},"assistantMetadata":{"type":"object","description":"Metadata associated with the connector assistant configuration."},"useTechAdaptorForm":{"type":"boolean","description":"When true, the UI presents the full technical adaptor form for this import instead of\nthe simplified assistant form."},"distributedAdaptorData":{"type":"object","description":"Internal state stored by distributed adaptors (such as the NetSuite SuiteApp) for this import."},"filter":{"description":"Filter applied to incoming records before they are sent to the destination system.\nRecords that match continue through the import; records that don't are silently\ndropped. Filter expressions reference the incoming record's fields.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"traceKeyTemplate":{"type":"string","description":"Handlebars template that overrides how each record's unique trace key is generated,\nused to track records through the flow and match errors to records. Trace keys are\ncapped at 256 characters; a longer key is stored truncated from the middle, keeping\nthe beginning and end of the value."},"mockResponse":{"type":"array","description":"Predefined response records used in place of calling the destination system when\ntesting the import, so flows can run without writing real data. Records must be in\nintegrator.io canonical format — the server rejects any other shape with a 422.","items":{"type":"object","properties":{"statusCode":{"type":"integer","description":"HTTP status code the mock returns (e.g. 200, 403)."},"id":{"type":["string","integer"],"description":"Identifier echoed for the mocked record."},"ignored":{"type":"boolean","description":"When true, the mocked record reports as ignored rather than imported."},"dataURI":{"type":"string","description":"Data URI echoed for the mocked record."},"errors":{"type":"array","description":"Mock error entries returned with the record."},"_json":{"type":["object","array"],"description":"Mock response body."},"_headers":{"type":"object","description":"Mock response headers."}}}},"_ediProfileId":{"type":"string","format":"objectId","description":"EDI profile this import uses to generate outbound X12 or EDIFACT documents — it supplies\nthe envelope qualifiers, delimiters, version, and validation rules. Set it when the\nimport produces EDI output; omit it otherwise. Accepted on the file-writing imports —\nFTP, AS2, S3 and HTTP file mode (`http.type: \"file\"`)."},"parsers":{"type":"array","description":"Legacy parser configuration slot. The server initializes this field to an empty array\nand current API writes never populate it; treat it as server bookkeeping rather than a\nsetting to configure."},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the import lifecycle for\ntransformations, validation, and custom result handling beyond what configuration alone\ncan express.","properties":{"preMap":{"type":"object","description":"Hook that runs on each page of records before the import's mappings are applied.\nCommonly used to reshape or filter records ahead of mapping.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postMap":{"type":"object","description":"Hook that runs after the import's mappings are applied but before records are sent\nto the destination system. Commonly used for adjustments that need the mapped\n(destination-shaped) record.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postSubmit":{"type":"object","description":"Hook that runs after the destination system responds, with access to both the\nsubmitted records and the destination's responses. Commonly used to inspect results\nor adjust the response data passed downstream.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postAggregate":{"type":"object","description":"Hook that runs after an aggregated file has been submitted to the destination, for\nfile-based imports that combine records into a single file.","allOf":[{"$ref":"#/components/schemas/Hook"}]}}},"sampleResponseData":{"type":["object","array","string"],"description":"Sample response payload used to preview response mappings and test downstream steps\nwithout calling the destination system."},"responseTransform":{"description":"Transformation that reshapes the destination system's response after records are\nimported, before the response is processed by response mappings and downstream\nsteps. Commonly used to extract relevant fields from verbose API responses.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"modelMetadata":{"type":"object","description":"Metadata about the destination data model captured for this import. Rarely set."},"mapping":{"type":"object","description":"Mapper 1.0 mapping configuration. `fields` maps individual fields; `lists` maps\nrecord lists, each with its own `fields` array. Superseded by Mapper 2.0\n(`mappings`) but still widely used.","properties":{"fields":{"type":"array","description":"Field-level mapping entries applied to each record.","items":{"$ref":"#/components/schemas/MappingField"}},"lists":{"type":"array","description":"List-level mappings, each generating a sublist on the target record.","items":{"type":"object","properties":{"generate":{"type":"string","description":"Target sublist or array path to generate."},"fields":{"type":"array","description":"Field mappings applied within each generated list item.","items":{"$ref":"#/components/schemas/MappingField"}}}}}}},"mappings":{"description":"Field mappings that transform incoming records into the destination system's field\nstructure — renaming fields, converting types, building nested objects, and applying\nformulas or lookups to derive values.","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"lookups":{"description":"Top-level mirror of the adaptor-specific ``lookups`` array. Celigo persists lookups\nin TWO locations on every import resource: ``<adaptor_key>.lookups`` (the typed,\nadaptor-specific definition) and this top-level ``lookups`` array (a permissive copy).","allOf":[{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the shape of the input passed to the import's processing pipeline."},"settingsForm":{"$ref":"#/components/schemas/Form"},"preSave":{"$ref":"#/components/schemas/PreSave"},"settings":{"$ref":"#/components/schemas/Settings"}}},"As2":{"type":"object","description":"Configures how outbound AS2 messages are built for this import. Required when the\n_connectionId field references an AS2 connection. AS2 (Applicability Statement 2) transmits\nEDI and other data securely over HTTP/S using S/MIME encryption and digital signatures; this\nobject names the payload file, the message identifier, retry behavior, and any extra headers.","required":["fileNameTemplate"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies the partner-specific EDI and AS2 configuration.\nWhen set, the import inherits the connector's pre-configured settings; omit to use only\nthe AS2 connection details."},"fileNameTemplate":{"type":"string","description":"Handlebars template that names the file carrying each AS2 message payload. Include a\nuniqueness token such as {{timestamp}} so concurrent or repeated sends do not collide;\ndefaults to file-{{timestamp}} in the form."},"messageIdTemplate":{"type":"string","description":"Handlebars template that generates the Message-ID header for each AS2 message. Placeholders\nare replaced at runtime; the resulting value must be globally unique and follow RFC 5322\nheader formatting. Leave unset to let the platform generate the Message-ID."},"maxRetries":{"type":"number","default":0,"description":"Number of times a failed transmission is retried after transient errors such as network\nfailures or timeouts; does not affect the initial send. The form offers 1–5; set to 0\n(the default) to report failure immediately without retrying."},"headers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header sent with the AS2 message (for example, `content-disposition`). Header names are case-insensitive."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions rendered against the record."}}},"description":"HTTP headers sent with the AS2 message transmission.\nSupports both standard AS2 headers and custom headers required by trading partner agreements."}}},"Dynamodb":{"type":"object","description":"Configuration for DynamoDB imports. Required when the _connectionId field references a\nDynamoDB connection; must not be included for other connection types. putItem writes a full\nitem document; updateItem modifies named attributes of an existing item identified by its\nkey.","required":["region","method","tableName"],"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"],"default":"us-east-1","description":"AWS region hosting the DynamoDB table. Determines the service endpoint used for all\nrequests and must match the region where the table is deployed."},"method":{"type":"string","enum":["putItem","updateItem"],"description":"Write operation performed against the DynamoDB table for each record."},"tableName":{"type":"string","description":"DynamoDB table that receives the imported records.\nMust match an existing table in the target AWS account and region; table names are case-sensitive."},"partitionKey":{"type":"string","description":"Value of the target item's partition key for the updateItem operation; together with\nsortKey (when the table defines one) it identifies the item each record writes to.\nSupports handlebars to reference the incoming record's fields."},"sortKey":{"type":"string","description":"Value of the target item's sort key; supports handlebars. Omit when the table defines only\na partition key."},"itemDocument":{"type":"string","description":"JSON document written to the table for the putItem operation, containing all attribute\nnames and values for the item, including its key attributes. Supports handlebars."},"updateExpression":{"type":"string","description":"DynamoDB update expression naming which attributes to set, add, or remove on the item for\nthe updateItem operation. Only the named attributes change; reference attribute names and\nvalues through the expressionAttributeNames and expressionAttributeValues placeholders."},"conditionExpression":{"type":"string","description":"Condition evaluated against the existing item before the write executes.\nWhen the condition evaluates to false, the operation is aborted and no changes are made."},"expressionAttributeNames":{"type":"string","description":"JSON placeholder-to-name map for attribute names referenced in expressions, for example {\"#N\": \"Name\"}.\nUse it when an attribute name is a reserved word or contains special characters; placeholder keys start with #."},"expressionAttributeValues":{"type":"string","description":"JSON placeholder-to-value map for attribute values referenced in expressions; placeholder\nkeys start with a colon (e.g. \":val\") and each value is a DynamoDB attribute-value object\nsuch as {\"S\": \"text\"}. Supports handlebars."},"ignoreExtract":{"type":"string","description":"Path to the field in the source record used to determine whether the record already exists.\nOnly used when ignoreExisting is set on the import."}},"if":{"properties":{"method":{"const":"updateItem"}},"required":["method"]},"then":{"required":["partitionKey","updateExpression"]}},"Http":{"type":"object","description":"Configuration for HTTP imports.\nRequired whenever the connection referenced by _connectionId has type http, and on\n`NetSuiteHTTPImport` (NetSuite's REST APIs on a `netsuite` connection, where `nsDomainType`\nsupplies the host).","properties":{"sendPostMappedData":{"type":"boolean","default":true,"description":"When true, the request body is the record as reshaped by the import's mapping step. When false, the original pre-mapped record is sent instead, bypassing the mappings for the payload."},"isRest":{"type":"boolean","description":"Internal flag indicating the import was built with the legacy REST form rather than the unified HTTP framework. Set by the form; not configured directly."},"strictHandlebarEvaluation":{"type":"boolean","description":"When true, handlebars expressions that reference a missing field raise an error instead of rendering an empty string, surfacing mapping mistakes early rather than sending blank values."},"formType":{"type":"string","enum":["assistant","http","rest","graph_ql","assistant_graphql"],"description":"Determines the UI form and configuration experience for this import."},"type":{"type":"string","enum":["file","records"],"description":"Controls whether the import sends structured record data or raw file content.\nMost HTTP imports use records; use file only when importing raw file content that will be processed downstream."},"requestMediaType":{"type":"string","enum":["xml","json","csv","urlencoded","form-data","octet-stream","plaintext"],"description":"Content type used to serialize the request body sent to the target API.\nMost REST APIs use json."},"_httpConnectorEndpointIds":{"type":"array","readOnly":true,"items":{"type":"string","format":"objectId"},"description":"HTTP connector endpoint IDs used by this import (multiple endpoints for different request\ntypes or operations). Set by the connector framework; client-supplied values are ignored."},"blobFormat":{"type":"string","enum":["utf8","ucs2","utf-16le","ascii","binary","base64","hex"],"description":"Character encoding format for blob/binary data imports.\nOnly relevant when type is \"file\" or when handling binary content.\n"},"batchSize":{"type":"integer","description":"Maximum number of records submitted per HTTP request, which affects throughput and API rate limiting.\nREST services typically send one record per request; raise it only for batch endpoints or RPC/XML services that accept multiple records, consulting the target API documentation for the optimal value.\n\nThe default varies by API."},"successMediaType":{"type":"string","enum":["xml","json","plaintext"],"description":"Media type expected in successful responses from the target API. Most APIs return json."},"requestType":{"type":"array","items":{"type":"string","enum":["CREATE","UPDATE"]},"description":"Operation type each request performs; for composite (upsert) imports, include both values.\nThe array is positionally aligned with the relativeURI and method arrays — each index maps to one operation — and the existingExtract field determines which operation runs at runtime."},"errorMediaType":{"type":"string","enum":["xml","json","plaintext"],"description":"Media type expected in error responses from the target API. Most APIs return json."},"followRedirects":{"type":"boolean","default":true,"description":"When explicitly false, 3xx responses are not followed — the redirect\nresponse itself (status code, `Location` header, body) is what the\nimport records as the result. Omitted or true follows allowed\nredirects (the default behavior)."},"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":"array","items":{"type":"string"},"description":"Relative URI path for each import request, as an array of strings (never an object); values can include Handlebars expressions for dynamic segments.\nTemplates render against the pre-mapped record — the original input before the Import's mapping step — because URI construction usually needs business identifiers that mappings may rename or remove.\nFor composite (upsert) imports, the array is positionally aligned with method and requestType, and existingExtract decides the index used at runtime: the UPDATE index when its field has a value, the CREATE index when it is empty or missing.\nReference the existing record ID in the UPDATE URI with `{{{data.0.fieldName}}}` (triple braces with the data.0 prefix).\nUse separate array elements per operation rather than `{{#if}}` conditionals inside a single URI."},"method":{"type":"array","items":{"type":"string","enum":["GET","PUT","POST","PATCH","DELETE"]},"description":"HTTP method used for each import request.\nFor composite (upsert) imports, the array is positionally aligned with relativeURI and requestType — e.g. `[\"PUT\", \"POST\"]` with `requestType: [\"UPDATE\", \"CREATE\"]` uses PUT for updates and POST for creates."},"_httpConnectorVersionId":{"type":"string","format":"objectId","readOnly":true,"description":"HTTP connector version used by this import. Set by the connector framework; client-supplied values are ignored (write-tested)."},"_httpConnectorResourceId":{"type":"string","format":"objectId","readOnly":true,"description":"HTTP connector resource used by this import. Set by the connector framework; client-supplied values are ignored (write-tested)."},"_httpConnectorEndpointId":{"type":"string","format":"objectId","readOnly":true,"description":"HTTP connector endpoint used by this import (single endpoint). Set by the connector framework; client-supplied values are ignored (write-tested)."},"body":{"type":"array","items":{"type":["string","null"]},"description":"Request body template for the import — an array of Handlebars template strings\npositionally aligned with the `method` array (e.g. `[\"{{{record}}}\"]`). A `null`\nentry means no template for the method at that position. Each entry is a string:\nwrite a JSON body as a template string (`[\"{\\\"id\\\": \\\"{{record.id}}\\\"}\"]`), not as\na JSON object, because an object entry does not render as JSON at runtime.\nLeave undefined for standard imports — the Import's mapping step transforms the source record and the mapped data is sent as the request body automatically.\nSet it only when the target API requires XML, SOAP, or a custom structure that mappings cannot produce, or when the user explicitly specifies a request body.\nWhen set, the mapping step still runs first and the template renders against the post-mapped record — use `{{record.<mappedField>}}` to reference mapping outputs.\nHand-templated bodies are harder to maintain and debug than mappings; when in doubt, leave this field undefined.\n\nCustom envelopes can combine static header or account fields with per-message\nfield references inside a loop.\n\nStatic wrappers around the mapped record are appropriate for message-bus class envelopes,\nHMAC signature wrappers, integration batch headers,\nnamed single-record array wrappers, and similar destination-required structures.\n\nBatch templates can wrap an envelope, such as a header, version, or marketplace,\naround serialized records.\n\nUse this pattern when each message needs a small number of fields rather than the whole record."},"ignoreEmptyNodes":{"type":"boolean","description":"When true, fields with empty values are stripped from the request body before it is sent,\nso the target API receives only populated fields. Shown as \"Remove empty fields from HTTP\nrequest body\" in the UI."},"existingExtract":{"type":"string","description":"Field name or JSON path that drives the upsert decision for composite imports with both CREATE and UPDATE in requestType.\nWhen the field has a value in the incoming record, the UPDATE operation's relativeURI and method are used; when it is empty or missing, the CREATE operation runs.\nMust match a field populated by an upstream lookup or response mapping (e.g. a destination system ID like \"shopifyCustomerId\"); omit for single-operation imports."},"existingLookupName":{"type":"string","description":"Name of an entry in `lookups` that resolves whether each record already exists in the destination, driving the composite upsert decision when `existingExtract` alone is insufficient (e.g. the check requires a call to the target system). Paired with composite CREATE/UPDATE imports.\n\nUse `existingLookupName` when the incoming record has no destination ID and\nneeds a per-record probe to discover one;\nuse `existingExtract` when the record already carries the ID.\n\nFor single-operation imports,\nuse `ignoreExtract` or `ignoreLookupName` with `ignoreExisting` or\n`ignoreMissing` to skip records instead of routing them.\n\nThe lookup pattern adds one HTTP request per record at runtime,\nmaking it better suited to one-off migrations and low-volume reconciliation than\nhigh-volume recurring syncs."},"ignoreExtract":{"type":"string","description":"Field name on the incoming record that drives the SKIP-vs-PROCESS\ndecision when ``ignoreExisting`` or ``ignoreMissing`` is set.\n\nPairs with either of the two skip-mode flags:\n\n- With ``ignoreExisting: true`` (CREATE-only imports that should\n  avoid duplicates): if ``ignoreExtract`` has a value on the\n  record, the record is treated as already-existing and is\n  skipped; if empty, the record is created.\n- With ``ignoreMissing: true`` (UPDATE-only imports that should\n  avoid creating new records): if ``ignoreExtract`` has a value,\n  the record is treated as existing and is updated; if empty,\n  the record is skipped.\n\n``ignoreExtract`` checks a field that's already on the record —\nzero per-record HTTP overhead.  Use ``ignoreLookupName`` instead\nwhen the record has no field to inspect and the existence check\nneeds a per-record probe against the destination.\n\n**Mutually exclusive with ``ignoreLookupName``**\n\nSet EXACTLY ONE of ``ignoreExtract`` / ``ignoreLookupName`` when\n``ignoreExisting`` or ``ignoreMissing`` is enabled.\n\n**When not to set this field**\n\nOnly valid for single-operation imports with either\n``ignoreExisting: true`` or ``ignoreMissing: true`` set.\nComposite (upsert) imports — those with both ``CREATE`` and\n``UPDATE`` in ``requestType`` — use ``existingExtract`` /\n``existingLookupName`` instead, because they route records\nbetween two write paths rather than skipping them."},"endPointBodyLimit":{"type":"integer","description":"Maximum size limit for the request body in bytes.\nUsed to enforce API-specific size constraints.\n"},"headers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header to include with each import request."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions rendered against the pre-mapped record."}}},"description":"Custom HTTP headers included with import requests; values can contain Handlebars expressions.\nValue templates render against the pre-mapped record (the original input before the Import's mapping step) — use `{{record.<field>}}` to reference fields as they appear in the upstream source."},"response":{"type":"object","properties":{"resourcePath":{"type":"array","items":{"type":"string"},"description":"Per-operation JSON path to the resource collection in each\nresponse.  Outer-array length MUST match ``requestType``\nlength: 1 entry for single-op imports, N entries for an\nN-operation composite import (duplicate the value when\noperations agree).  See the ``response`` object docs\nabove for the full positional-array contract.\n\nRequired when ``batchSize > 1`` because the platform needs\nto know where the array of result records lives inside the\nresponse envelope.  When the API returns a bare array at\nthe top level, leave this field unset.\n\nEach entry is a DOT-PATH STRING (e.g. ``\"data.results\"``,\n``\"items\"``) — do NOT split paths into segments across\narray elements."},"resourceIdPath":{"type":"array","items":{"type":["string","null"]},"description":"Per-operation JSON path to the unique ID field within each\nrecord in the response.  Outer-array length MUST match\n``requestType`` length — duplicate the value when both\noperations agree (the common case) rather than collapsing\nto a 1-entry array.\n\nEach entry is a DOT-PATH STRING, or null for an operation\nwith no ID path configured. When not specified, the platform\nlooks for standard `id` or `_id` fields automatically."},"successPath":{"type":"array","items":{"type":"string"},"description":"Per-operation JSON path to a field that indicates whether\nthe API call succeeded.  Outer-array length MUST match\n``requestType`` length — duplicate the value when both\noperations agree rather than collapsing to a 1-entry\narray on a composite import.\n\nUse this when the API returns HTTP 200 for everything and\nsignals success / failure through a body field.  Pairs with\n``successValues`` to define which values at this path mean\nsuccess.\n\nEach entry is a DOT-PATH STRING."},"successValues":{"type":"array","items":{"type":"array","items":{"type":"string"}},"description":"Per-operation list of values at ``successPath`` that mean\nsuccess.  TWO levels of array nesting:\n\n- Outer array: one entry per ``requestType`` index.\n  Length MUST match ``requestType`` length — duplicate\n  the inner array when both operations agree rather than\n  collapsing to a 1-entry outer array on a composite\n  import.\n- Inner array: the list of acceptable success values for\n  that operation.\n\nAll comparisons are STRING comparisons regardless of the\nactual response value type — the platform stringifies\nnumbers, booleans, etc. before comparing."},"failPath":{"type":"array","items":{"type":"string"},"description":"Per-operation JSON path to a field that signals failure\neven when HTTP returns 200.  Outer-array length MUST\nmatch ``requestType`` length — duplicate when both\noperations agree.  Same array semantics as ``successPath``.\n\nSome APIs use only ``successPath`` (failure = absence of\nsuccess); others publish both an explicit success indicator\nand an explicit failure indicator.  When both are\navailable, set both — the platform evaluates ``successPath``\nfirst and falls through to ``failPath`` only when the\nsuccess check is inconclusive."},"failValues":{"type":"array","items":{"type":"array","items":{"type":"string"}},"description":"Per-operation list of values at ``failPath`` that mean\nfailure.  Same two-level array shape and length contract\nas ``successValues`` — outer length MUST match\n``requestType`` length, duplicate the inner array on\ncomposite imports when both operations agree.\n\nOften used as the inverse of ``successValues`` (e.g.\n``successValues: [[\"true\"]]`` paired with\n``failValues: [[\"false\"]]``)."},"errorPath":{"type":"string","description":"JSON path to the error message inside the response body.\n\n**Note**: this field is NOT a per-operation array — it's a\nsingle string applied to every operation.  Most APIs use\nthe same error-message field shape for both UPDATE and\nCREATE responses, so the platform doesn't expose a per-op\noverride here."},"allowArrayforSuccessPath":{"type":"boolean","description":"When true, allows array values at successPath during success evaluation."},"hasHeader":{"type":"boolean","description":"When true, treats the first record in the response as a header row (for CSV responses)."}},"description":"Configuration for parsing and interpreting HTTP responses returned\nfrom each write request the import issues.\n\n**Critical: positional per-operation arrays**\n\nEvery field inside ``response`` (``resourcePath``, ``resourceIdPath``,\n``successPath``, ``successValues``, ``failPath``, ``failValues``)\nis an ARRAY whose outer length is positionally aligned with\n``requestType`` / ``method`` / ``relativeURI``.  This is the\nSAME positional-alignment contract as those three sibling\narrays — see ``relativeURI`` docs for the full explanation.\n\nEach outer-array index describes how to parse the response from\nthe operation at that same index in ``requestType``:\n\n- **Single-operation import** (``requestType: [\"CREATE\"]``):\n  every response field is a 1-element array.\n  ``successPath: [\"ok\"]`` means \"for the one operation,\n  look at path ``ok`` to detect success\".\n- **Composite upsert** (``requestType: [\"UPDATE\", \"CREATE\"]``):\n  every response field is a 2-element array.  Index 0 parses\n  the UPDATE response, index 1 parses the CREATE response.\n\n**successValues / failValues — array of arrays**\n\n``successValues`` and ``failValues`` carry an extra array level\nbecause each operation can have MULTIPLE acceptable values:\n\n- Outer array: one entry per operation (positional, as above).\n- Inner array: the list of acceptable values for that operation.\n\nExample:\n```json\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"successPath\": [\"ok\", \"ok\"],\n\"successValues\": [[\"true\", \"1\"], [\"true\"]]\n```\nMeans: the UPDATE response is successful when ``ok`` is either\n``\"true\"`` or ``\"1\"``; the CREATE response is successful only\nwhen ``ok`` is ``\"true\"``.\n\n**Always duplicate when operations agree (REQUIRED for composite)**\n\nThe outer-array length MUST match ``requestType`` length on\nevery response field.  When the UPDATE and CREATE responses\nof the same connector look the same (which is the common\ncase — most APIs return the same shape from both endpoints),\nDUPLICATE the entry so the array has the same length as\n``requestType``.  Do NOT collapse to a 1-entry array on a\ncomposite import.\n\n```json\n// ✅ Correct — explicit per-operation entries, even when identical\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data.id\", \"data.id\"],\n\"successPath\": [\"success\", \"success\"],\n\"successValues\": [[\"true\"], [\"true\"]]\n```\n\n```json\n// ❌ Wrong — 1-entry array on a composite import\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data.id\"],\n\"successPath\": [\"success\"],\n\"successValues\": [[\"true\"]]\n```\n\nWhy duplicate even when they agree:\n\n- **UI parity.**  The Celigo UI renders one response-handling\n  form per ``requestType`` index.  A 1-entry array on a\n  composite import leaves the second operation's form panel\n  empty, which looks like a half-built config to the user\n  reviewing the saved import — even if it works at runtime.\n- **Symmetric shape rule.**  ``relativeURI`` / ``method`` /\n  ``requestType`` REQUIRE the entry count to match across\n  all three (the runtime decider uses positional alignment).\n  Making ``response.*`` follow the same \"outer length ==\n  requestType length\" rule means there's ONE shape rule to\n  remember, not two.\n- **Unambiguous intent.**  A 2-entry duplicated array\n  documents in the saved config that the developer\n  considered both operations.  A 1-entry array could mean\n  \"both ops use this\" or could mean \"I forgot to fill in\n  the second op.\"  Duplication eliminates the ambiguity.\n\nWhen operations DIFFER (rare — different response shapes\nper endpoint), set distinct values at each index.  Either\nway the outer length always matches ``requestType``.\n\n**Common llm mistake (DO not do THIS)**\n\nDo NOT split a single dot-path into array segments thinking the\nouter array represents path segments:\n\n```json\n// WRONG — the LLM expressed \"data.id\" as path segments\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data\", \"id\"]\n```\n\nThis is interpreted by the platform as: UPDATE response's id is\nat path ``data``, CREATE response's id is at path ``id`` — almost\nnever what the user wants.  The CORRECT shape is the single\ndot-path expressed as ONE string per operation, duplicated to\nmatch ``requestType`` length:\n\n```json\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data.id\", \"data.id\"]\n```"},"_asyncHelperId":{"type":"string","format":"objectId","description":"Reference to an AsyncHelper resource that polls an asynchronous\ndestination API on this import's behalf.\n\nSet this ONLY when the destination API is genuinely asynchronous — it\nacknowledges the submitted payload (HTTP 202, a job ticket) and ingests\nit in the background, so completion must be polled for (e.g. bulk\nimage/file ingestion, large bulk-load endpoints). Most imports are\nsynchronous and need NO async helper; adding one to a synchronous\ndestination just adds polling overhead plus a status and result export\nto maintain. When in doubt, leave it unset.\n\nThe referenced helper bundles the polling config plus a required status\nexport (polled to check progress) and a result export (fetches the\nfinal payload). An import configured with an async helper cannot carry\nits own transform, output filter, or preSavePage hook — put that\nprocessing in the result export instead."},"ignoreLookupName":{"type":"string","description":"Name of an entry in `lookups` used to check whether each record already exists, so `ignoreMissing`/`ignoreExisting` can skip it. Use it instead of `ignoreExtract` when existence must be resolved by a call to the target system rather than read from a field on the incoming record."},"lookups":{"description":"Lookups referenced by `existingLookupName`/`ignoreLookupName` to resolve record existence against the destination system at runtime.","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia ``lookupName`` and Handlebars expressions reference it via\n``{{lookup.name}}``. Must be unique within this\nresource's ``lookups`` array.\n"},"method":{"type":"string","enum":["GET","POST","PUT","PATCH","DELETE"],"description":"HTTP method used to issue the lookup request. ``GET`` and\n``POST`` are most common. Omit when using a static ``map`` or\n``_lookupCacheId``.\n"},"relativeURI":{"type":"string","description":"HTTP path (relative to the connection's base URL) that\nresolves the lookup. Use Handlebars with triple braces to\ninject values from the incoming record — e.g.\n``/customers?email={{{email}}}`` or\n``/accounts/{{{accountId}}}``.\n"},"postBody":{"type":"string","description":"Request body template for POST / PUT / PATCH lookup methods.\nTypically a JSON string with Handlebars placeholders —\n``{\"email\":\"{{{email}}}\"}``. Ignored for GET / DELETE.\n"},"headers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}},"description":"Optional custom headers to attach to the lookup request.\nUseful for endpoints that need a different Accept header or\nan auxiliary auth token separate from the connection-level\nheaders.\n"},"extract":{"type":"string","description":"JSONPath expression that selects the lookup value from the\nHTTP response body. ``$.`` prefix optional. Examples:\n``$.data[0].id``, ``$.results.primaryKey``, ``id``.\n"},"_lookupCacheId":{"type":"string","format":"objectId","description":"Optional reference to a LookupCache resource. When set, the\nlookup reads from the cache instead of issuing a live HTTP\nrequest — useful for stable reference data that should be\npre-loaded.\n"},"map":{"type":["object","null"],"description":"Optional static key→value object evaluated BEFORE the HTTP\nrequest. When the incoming value matches a key, the mapped\nvalue is returned without hitting the remote endpoint. Null\non dynamic lookups with no static map.\n"},"default":{"type":["string","null"],"description":"Value returned when the HTTP request returns no usable\nmatch. When omitted and ``allowFailures`` is false, an\nunmatched lookup halts the record. Stored as null when no\nfallback is configured.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, a lookup miss (no matching response and no\n``default``) resolves to ``null`` and the import continues.\nWhen false (default), a miss halts the record. May be\nstored as null (treated as unset).\n"},"useDefaultOnMultipleMatches":{"type":"boolean","description":"Controls behaviour when the response contains more than one\nmatch. When true, the ``default`` value is returned instead\nof raising an ambiguous-match error. When false (default),\nmultiple matches are treated as a failure.\n"}}},"type":"array"}}},"Ftp":{"type":"object","description":"Defines where files are written on 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 target folder and the file naming comes from\nthe import's file configuration.","required":["directoryPath"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies partner-specific B2B settings for this import.\nWhen set, the import inherits the connector's pre-configured settings; omit to use only\nthe FTP connection details."},"directoryPath":{"type":"string","description":"Directory on the server where imported files are written, either absolute or relative to\nthe login directory; the FTP user must have write permission on it. Use forward slashes\nregardless of server OS — paths are case-sensitive on UNIX/Linux servers. Supports\nhandlebars templates."},"fileName":{"type":"string","description":"Name of the file written to the server, including its extension; do not include directory\nseparators — the location comes from directoryPath. Supports handlebars placeholders such\nas items-{{timestamp}}.csv to generate a unique name per run."},"inProgressFileName":{"type":"string","description":"Temporary name the file carries while its upload is in progress, preventing other systems\nfrom processing a partially transferred file; it is renamed to its final name once the\nupload completes. Include a handlebars uniqueness token (e.g. {{timestamp}}) when one file\nis written per flow run."},"backupDirectoryPath":{"type":"string","description":"Directory on the same server where a copy of each written file is retained after a\nsuccessful import; if omitted, no server-side backup is kept. Supports static paths or\nhandlebars templates."}}},"Jdbc":{"type":"object","description":"Configuration for JDBC import operations. Defines how data is written to a database\nvia a JDBC connection.\n\n**Query type determines which fields are required**\n\n| queryType        | Required fields              | Do NOT set        |\n|------------------|------------------------------|-------------------|\n| [\"per_record\"]   | query (array of SQL strings) | bulkInsert        |\n| [\"per_page\"]     | query (array of SQL strings) | bulkInsert        |\n| [\"bulk_insert\"]  | bulkInsert object            | query             |\n| [\"bulk_load\"]    | bulkLoad object              | query             |\n\n**Critical:** query IS AN ARRAY OF STRINGS\nThe query field must be an array of plain strings, NOT a single string and NOT an array of objects.\nCorrect: [\"INSERT INTO users (name) VALUES ('{{{name}}}')\"]\nWrong: \"INSERT INTO users ...\"\nWrong: [{\"query\": \"INSERT INTO users ...\"}]","required":["queryType"],"properties":{"query":{"type":"array","items":{"type":"string"},"description":"Array of SQL query strings to execute. REQUIRED when queryType is [\"per_record\"] or [\"per_page\"].\n\nEach element is a complete SQL statement as a plain string. Typically contains a single query.\n\nUse Handlebars {{fieldName}} syntax to inject values from incoming records.\n\n**Format — array of strings**\n- CORRECT: [\"INSERT INTO users (name, email) VALUES ('{{{name}}}', '{{{email}}}')\"]\n- WRONG: \"INSERT INTO users ...\"  (not an array)\n- WRONG: [{\"query\": \"INSERT INTO users ...\"}]  (objects are invalid — causes Cast error)\n\n**Examples**\n- INSERT: [\"INSERT INTO users (name, email) VALUES ('{{{name}}}', '{{{email}}}')\"]\n- UPDATE: [\"UPDATE inventory SET qty = {{{quantity}}} WHERE sku = '{{{sku}}}'\"]\n- MERGE/UPSERT: [\"MERGE INTO target USING (SELECT CAST(? AS VARCHAR) AS email) AS src ON target.email = src.email WHEN MATCHED THEN UPDATE SET name = ? WHEN NOT MATCHED THEN INSERT (name, email) VALUES (?, ?)\"]\n\n**String vs numeric values in handlebars**\n- Use triple braces so the raw value is emitted; double braces emit the value already single-quoted, so wrapping them in quotes double-quotes it.\n- Strings: supply the quotes yourself — '{{{name}}}'\n- Numbers: no quotes — {{{quantity}}}"},"queryType":{"type":"array","items":{"type":"string","enum":["bulk_insert","per_record","per_page","bulk_load"]},"description":"Execution strategy for the SQL operation. REQUIRED. Must be an array with one value.\n\n**Decision tree**\n\n1. If UPDATE or UPSERT/MERGE → [\"per_record\"] (set query field)\n2. If INSERT with \"ignore existing\" / \"skip duplicates\" / match logic → [\"per_record\"] (set query field)\n3. If pure INSERT with no duplicate checking → [\"bulk_insert\"] (set bulkInsert object)\n4. If high-volume bulk load → [\"bulk_load\"] (set bulkLoad object)\n\n**Critical relationship to other fields**\n| queryType        | REQUIRES              | DO NOT SET   |\n|------------------|-----------------------|--------------|\n| [\"per_record\"]   | query (array)         | bulkInsert   |\n| [\"per_page\"]     | query (array)         | bulkInsert   |\n| [\"bulk_insert\"]  | bulkInsert object     | query        |\n| [\"bulk_load\"]    | bulkLoad object       | query        |\n\n**Examples**\n- Per-record upsert: [\"per_record\"]\n- Bulk insert: [\"bulk_insert\"]\n- Bulk load: [\"bulk_load\"]\n"},"bulkInsert":{"type":"object","description":"Bulk insert configuration. REQUIRED when queryType is [\"bulk_insert\"]. DO NOT SET when queryType is [\"per_record\"].\n\nEnables efficient batch insertion of records into a database table without per-record SQL.\n","properties":{"tableName":{"type":"string","description":"Target database table name for bulk insert. REQUIRED.\nCan include schema qualifiers (e.g., \"schema.tableName\").\n"},"batchSize":{"type":"string","description":"Number of records per batch during bulk insert.\nLarger values improve throughput but use more memory.\nCommon values: \"1000\", \"5000\".\n"}}},"bulkLoad":{"type":"object","description":"Bulk load configuration. REQUIRED when queryType is [\"bulk_load\"]. Uses database-native bulk loading for maximum throughput.\n","properties":{"tableName":{"type":"string","description":"Target database table name for bulk load. REQUIRED.\nCan include schema qualifiers (e.g., \"schema.tableName\").\n"},"primaryKeys":{"type":["array","null"],"items":{"type":"string"},"description":"Primary key column names for upsert/merge during bulk load.\nWhen set, existing rows matching these keys are updated; non-matching rows are inserted.\nExample: [\"id\"] or [\"order_id\", \"product_id\"] for composite keys.\n"},"overrideMergeOrInsertQuery":{"type":"boolean","description":"When true, a custom merge/insert query replaces the auto-generated statement for the\nbulk load, enabling ignore-existing logic or conditional updates."}}},"lookups":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia `lookupName` and Handlebars expressions reference it via\n`{{lookup \"name\" value}}`. Must be unique within this\nresource's `lookups` array."},"query":{"type":"string","description":"SQL query for the lookup (e.g., \"SELECT id FROM users WHERE email = '{{{email}}}'\")."},"extract":{"type":"string","description":"JSONPath-style expression that selects the value from the\nquery result row (e.g. `id`, `details.price`). Omit when\nthe query returns a single scalar."},"_lookupCacheId":{"type":"string","format":"objectId","description":"Optional reference to a LookupCache resource. When set, the\nlookup reads from the cache instead of issuing a live SQL\nquery — useful for stable reference data that should be\npre-loaded."},"allowFailures":{"type":"boolean","description":"When true, a lookup miss (no matching row and no `default`)\nresolves to `null` and the import continues. When false\n(default), a miss halts the record."},"map":{"type":"object","description":"Optional static key→value object evaluated BEFORE the SQL\nquery. When the incoming value matches a key, the mapped\nvalue is returned without querying the database."},"default":{"type":"string","description":"Value returned when the query matches no rows. When omitted\nand `allowFailures` is false, an unmatched lookup halts the\nrecord."}}},"description":"Lookup definitions executed against the JDBC connection. Each entry\nruns a `SELECT` and extracts a value used by field mappings\n(referenced via `lookupName`) or Handlebars expressions\n(referenced via `{{lookup \"name\" value}}`)."}}},"Mongodb":{"type":"object","description":"Configuration for MongoDB imports. Contains only the MongoDB-specific properties; import-level fields such as ignoreExisting, ignoreMissing, name, and description are set on the parent import, not here.","required":["method","collection"],"properties":{"method":{"type":"string","enum":["insertMany","updateOne"],"description":"Write operation performed against the MongoDB collection for each record."},"collection":{"type":"string","description":"MongoDB collection that receives the imported records.\nCollection names are case-sensitive and must not start with the reserved \"system.\" prefix."},"filter":{"type":"string","description":"MongoDB query filter that selects the document each updateOne applies to; required when\nmethod is updateOne. Supports standard MongoDB query operators and dot notation, and\nhandlebars placeholders to reference the incoming record's fields."},"document":{"type":"string","description":"Content of the document written to the collection (used when method is insertMany), as a JSON string that can include nested documents and arrays and handlebars placeholders."},"update":{"type":"string","description":"Modifications applied to matching documents (used when method is updateOne), using MongoDB update operators such as $set, $inc, or $push.\nA complete document without operators replaces the matched document entirely."},"upsert":{"type":"boolean","description":"When true, inserts a new document built from the update criteria if no existing document matches the filter.\nWhen false, records with no matching document are skipped and nothing is inserted."},"ignoreExtract":{"type":"string","description":"Path to the field in the source record that identifies existing records. When that field has a value, the record is treated as existing and skipped.\nOnly used when the import-level ignoreExisting flag is true; do not set it otherwise.\nEnclose field names containing special characters in square brackets (for example, [vendor-code]), and reference array items with an index, such as items[0].id."},"ignoreLookupFilter":{"type":"string","description":"JSON-stringified MongoDB query filter used to find existing documents in the target collection when the import-level ignoreExisting flag is true.\nIf the query matches a document, the incoming record is treated as existing and skipped.\nUse Handlebars placeholders to reference values from the incoming record, for example \"{\\\"email\\\":\\\"{{email}}\\\"}\".\nUnlike ignoreExtract, which only checks whether a field on the incoming record has a value, this filter queries the MongoDB collection itself."}},"if":{"properties":{"method":{"const":"updateOne"}},"required":["method"]},"then":{"required":["filter"]}},"NetSuite":{"type":"object","description":"Configuration for NetSuite imports (legacy adaptor; use netsuite_da / NetSuiteDistributedImport for SuiteApp 2.0).","properties":{"lookups":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique name for this lookup, referenced from mappings and handlebars templates with `{{lookup 'name' value}}`."},"recordType":{"type":"string","description":"NetSuite record type searched by a dynamic lookup (for example, `customer`, `salesOrder`). Set with `searchField` or `expression`; omit for a static `map` lookup."},"searchField":{"type":"string","description":"NetSuite field the dynamic lookup searches on (for example, `externalid`). Must be a searchable field on `recordType`. Use this or `expression`."},"expression":{"type":"string","description":"NetSuite search expression evaluated at runtime to select matching records (for example, `[\"recipient\",\"is\",\"123\"]`). Use this or `searchField`."},"resultField":{"type":"string","description":"Field on the matched NetSuite record whose value the lookup returns (for example, `internalid`).\nSupports dot notation for nested fields."},"map":{"type":["object","null"],"description":"Static lookup table with input values as keys and their corresponding output values. Use instead of a NetSuite search for fixed value translations. The platform stores `null` here on search-driven lookups."},"default":{"type":["string","null"],"description":"Fallback value returned when the lookup finds no match. Used together with `allowFailures`. Stored as `null` when not configured."},"allowFailures":{"type":"boolean","description":"When true, an unmatched lookup falls back to `default` and processing continues; when false, an unmatched lookup fails the record."},"_id":{"type":"object","description":"Server-assigned unique identifier for this lookup entry.","readOnly":true}}},"description":"Lookup definitions used to resolve reference values during the import, using either a static `map` or a dynamic NetSuite record search."},"operation":{"type":"string","enum":["add","update","addupdate","attach","detach","delete"],"description":"Operation to perform on NetSuite records, as a plain string (the API lowercases the value).\nFor `update`, `addupdate`, and `delete`, also set internalIdLookup so existing records can be found; for `add` with ignoreExisting, set internalIdLookup to check for duplicates before creating.\nDefault to `addupdate` when the intent is \"sync\", \"upsert\", or \"create or update\"; use `add` for plain \"create\" or \"insert\".\nDo not wrap the value in an object like `{\"type\": \"addupdate\"}` — that fails validation."},"isFileProvider":{"type":"boolean","description":"When true, enables file operations against NetSuite — browsing, uploading, downloading, updating, and deleting files.\nWhen false or omitted, file-related operations are unavailable through this import."},"customFieldMetadata":{"type":"object","description":"Metadata describing the custom fields defined on the target NetSuite record type, used to validate and process custom field data during the import."},"recordType":{"type":"string","description":"NetSuite record type this import writes to (e.g. customer, salesOrder, invoice).\nUse the exact NetSuite internal record type identifier; both standard and custom record types are supported."},"recordTypeId":{"type":"string","description":"The unique identifier specifying the record type within NetSuite. Determines the applicable schema, fields, validation rules, and processing logic. Use exact NetSuite record type identifiers such as standard types or custom record IDs."},"retryUpdateAsAdd":{"type":"boolean","description":"When true, an update that fails because the target record does not exist is automatically retried as an add.\nWhen false or unset, update failures error immediately without retrying.\nMay create duplicate records if the update failed for a reason other than a missing record."},"batchSize":{"type":"number","description":"Number of records sent to NetSuite per API call.\nLarger batches reduce the number of calls but increase memory use and timeout risk per call."},"internalIdLookup":{"type":"object","properties":{"extract":{"type":"string","description":"Path used to extract the value for the internal ID lookup."},"searchField":{"type":"string","description":"The NetSuite record field used as the key attribute for the internal ID lookup search. Should be a unique or indexed field to ensure accurate matches. Must correspond to a valid, searchable field on the target record type."},"expression":{"type":"string","description":"NetSuite search expression that filters which records match the lookup.\nSupports logical operators (AND, OR, NOT), comparison operators, and nested conditions, evaluated at runtime against current data."}},"description":"Configuration for locating existing NetSuite records by internal ID.\nRequired when operation is `update`, `addupdate`, or `delete`; also used with `add` plus ignoreExisting to check for duplicates."},"preferences":{"type":"object","properties":{"ignoreReadOnlyFields":{"type":"boolean","description":"When true, silently skips read-only fields during update operations instead of raising errors."},"warningAsError":{"type":"boolean","description":"When true, treats NetSuite warnings as errors, causing the operation to fail immediately."},"skipCustomMetadataRequests":{"type":"boolean","description":"When true, skips custom metadata requests during NetSuite API operations to improve performance."}},"description":"Preferences that control how NetSuite handles the import operation."},"file":{"type":"object","properties":{"name":{"type":"string","description":"Filename for the file in the NetSuite File Cabinet, without any folder or path information.\nMust be unique within its folder; include the file extension (e.g. .pdf, .csv)."},"fileType":{"type":"string","description":"NetSuite file type that governs how the file is processed, stored, and displayed.\nMust match the actual content format of the file."},"folder":{"type":"string","description":"Folder in the NetSuite File Cabinet where the file is stored, as a numeric folder ID or a folder path.\nUpdating this on an existing file moves the file to the specified folder."},"folderInternalId":{"type":"string","description":"Internal ID of the File Cabinet folder where the file is stored.\nMust reference an existing folder; changing it moves the file to a different folder."},"internalId":{"type":"string","description":"NetSuite internal ID of an existing file, used to target it for retrieval, updates, or deletion.\nAssigned by NetSuite when the file is created and cannot be changed."},"backupFolderInternalId":{"type":"string","description":"Internal ID of the File Cabinet folder where backup files are stored."}},"description":"Configuration for the file to upload to or update in the NetSuite File Cabinet."}},"if":{"not":{"propertyNames":{"enum":["preferences","lookups"]}}},"then":{"required":["operation"],"if":{"properties":{"isFileProvider":{"const":true}},"required":["isFileProvider"]},"else":{"required":["recordType"]}}},"NetsuiteDistributed":{"type":"object","description":"Configuration for NetSuite Distributed (SuiteApp 2.0) import operations — the primary sub-schema for the NetSuiteDistributedImport adaptorType. The API field name is \"netsuite_da\".\noperation and recordType are required. The server may attach empty bookkeeping stubs of\nthis object (empty lookups/mapping, or missingOrCorruptedDAConfig) to imports of other\nadaptor types; stubs omit these fields. Set internalIdLookup so existing records\ncan be found when operation is update, addupdate, or delete.","properties":{"operation":{"type":"string","enum":["add","update","addupdate","attach","detach","delete"],"description":"Operation to perform on the target NetSuite record, as a plain string. Required.\nFor `update`, `addupdate`, and `delete`, also set internalIdLookup so existing records can be found."},"recordType":{"type":"string","description":"NetSuite record type to import into (e.g. \"customer\", \"salesorder\", \"customrecord_myrecord\" for custom record types). Required.\nMust match a valid NetSuite record type identifier."},"recordIdentifier":{"type":"string","description":"Custom record identifier, used to identify the specific record type when\nimporting into custom record types.\n"},"restletVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"Version of the NetSuite RESTlet to use, as a plain string.\nDefaults to \"suiteapp2.0\" when useSS2Restlets is true, \"suitebundle\" otherwise; rarely needs to be set explicitly for modern integrations.\nThe version is fixed when the step is created — the Advanced selector is disabled on existing steps, and migrating an existing step to a different version means recreating or cloning it."},"useSS2Restlets":{"type":"boolean","description":"When true, uses SuiteScript 2.0 RESTlets and restletVersion defaults to \"suiteapp2.0\".\nDefaults to true for modern integrations.\nSet at step creation together with restletVersion — the same creation-time-only constraint applies (see restletVersion)."},"missingOrCorruptedDAConfig":{"type":"boolean","description":"When true, the Distributed Adaptor configuration is missing or corrupted.\nSet by the system — do not set manually."},"batchSize":{"type":"number","description":"Number of records to process per batch. Controls how many records are sent\nto NetSuite in a single API call. Typical values: 50-200.\n"},"internalIdLookup":{"type":"object","description":"Configuration for looking up existing NetSuite records to match against incoming data.\nRequired when operation is \"update\", \"addupdate\", or \"delete\".","properties":{"extract":{"type":"string","description":"Path in the source record to extract the lookup value from (e.g. \"internalId\" or \"externalId\")."},"searchField":{"type":"string","description":"NetSuite field to search against (e.g. \"externalId\", \"email\", \"name\", \"tranId\")."},"operator":{"type":"string","description":"Comparison operator for the lookup (e.g. \"is\", \"contains\", \"startswith\")."},"expression":{"type":"string","description":"NetSuite search expression for complex lookup conditions, used for multi-field or conditional lookups."}}},"hooks":{"type":"object","description":"Script hooks for custom processing at different stages of the import.\nEach hook references a SuiteScript file and function.\n","properties":{"preMap":{"type":"object","description":"Runs before field mapping is applied.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}},"postMap":{"type":"object","description":"Runs after field mapping, before submission to NetSuite.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}},"postSubmit":{"type":"object","description":"Runs after the record is submitted to NetSuite.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}}}},"mapping":{"type":"object","description":"NetSuite-specific Mapper 1.0 field mappings (the \"DA\" dialect).\nUsed by `NetSuiteDistributedImport` exclusively — NetSuite's\nDistributed Adaptor cannot consume generic v1 mappings or the\nMapper 2.0 shape because it needs sublist, subrecord, and\nconditional lookup semantics the generic schemas don't model.\n\nField-level shape: `fields` for body columns, `lists` for\nsublists / line-item arrays.","properties":{"fields":{"type":"array","items":{"type":"object","properties":{"extract":{"type":["string","null"],"description":"Path in the source record to extract the value from.\nUse dot notation for nested fields (e.g., \"address.city\").\nThe platform stores `null` here on fields driven by\n`hardCodedValue` instead of an extract path.\n"},"generate":{"type":"string","description":"The NetSuite field ID to write the value to (e.g., \"companyname\", \"email\", \"subsidiary\").\n"},"hardCodedValue":{"type":"string","description":"A static value to always use instead of extracting from source data.\nMutually exclusive with extract.\n"},"lookupName":{"type":"string","description":"Reference to a lookup defined in netsuite_da.lookups by name."},"dataType":{"type":"string","description":"Data type hint for the field value (e.g., \"string\", \"number\", \"date\", \"boolean\").\n"},"internalId":{"type":"boolean","description":"When true, the value is a NetSuite internal ID reference."},"immutable":{"type":"boolean","description":"When true, this field is only set on record creation, not on updates."},"discardIfEmpty":{"type":"boolean","description":"When true, skip this field mapping if the extracted value is empty."},"extractDateFormat":{"type":"string","description":"Date format of the extracted value (e.g., \"MM/DD/YYYY\", \"ISO8601\").\nUsed to parse date strings from source data.\n"},"extractDateTimezone":{"type":["string","null"],"description":"Timezone of the extracted date value (e.g., \"America/New_York\"). Stored as `null` when not configured."},"subRecordMapping":{"type":"object","description":"Nested Mapper 1.0 mapping for a NetSuite subrecord\nembedded in this field.  A subrecord is a child\nrecord that only exists inside a parent (e.g. the\nInventory Detail on a serialized-item line of a\nsales order); NetSuite reaches it through the\nparent.\n\n**Recursive structure.**  The nested `mapping`\nobject has the same `{fields, lists}` shape as the\ntop-level `netsuite_da.mapping`.  Recursion is\nallowed but rare.","properties":{"recordType":{"type":"string","description":"NetSuite's internal record-type id of the\nSUBRECORD (not the parent).  E.g.\n`inventorydetail`, `itempricing`,\n`mainaddress`.\n"},"jsonPath":{"type":"string","description":"JSONPath into the source record that selects the\ndata feeding this subrecord.  Semantics depend\non placement:\n* Line-level (on `lists[].fields[].`\n  `subRecordMapping`) — often `$` to reuse\n  the enclosing sublist line's row, or a path\n  into a nested array on that row.\n* Body-level (on `fields[].subRecordMapping`)\n  — a path into the source record root.\nDefaults to `$`.\n"},"mapping":{"type":"object","description":"Nested `{fields, lists}` mapping for the\nsubrecord.  Identical shape to the top-level\n`netsuite_da.mapping`.  Subrecords often have\nempty `fields` and only a sublist (e.g.\n`inventorydetail` → `inventoryassignment`).\n"},"lookups":{"type":"array","description":"Subrecord-scoped lookups.  Same shape as the\ntop-level `netsuite_da.lookups`.  Fields inside\nthis `subRecordMapping.mapping` reference them\nvia `lookupName`.  Often `[]`.\n"},"referenceFieldId":{"type":"string","description":"Secondary reference-field identifier used on a\nsmall fraction of subrecord blocks in production\n(~3%).  Exact semantics are not publicly\ndocumented by Celigo — match the value on\nexisting resources when present; do NOT\nsynthesize one.  Leave unset unless a template\nclearly requires it.\n"}}},"conditional":{"type":"object","description":"Conditional logic for when to apply this field mapping\n(\"Only perform mapping when\" in the UI). Lookup-based\nconditions evaluate the lookup named by `lookupName`,\nwhich must be defined on this import.\n","properties":{"lookupName":{"type":"string","description":"Lookup to evaluate for the condition. Must exactly match the\n`name` of a lookup defined on this import."},"when":{"type":"string","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","ignore_if_set"],"description":"Condition that controls when this field mapping is applied."}}}}},"description":"Body-level field mappings. Each entry maps a source field to a NetSuite body field.\n"},"lists":{"type":"array","items":{"type":"object","properties":{"generate":{"type":"string","description":"The NetSuite sublist ID (e.g., \"item\" for sales order line items,\n\"addressbook\" for address sublists).\n"},"jsonPath":{"type":"string","description":"LEGACY — do not set. Nominally the JSON path to the array of sublist records, but NetSuite DA ignores a top-level jsonPath here."},"fields":{"type":"array","items":{"type":"object","properties":{"extract":{"type":["string","null"],"description":"Path from the source record root that produces\nthe value for this sublist column. Paths are\nalways anchored at the source record root —\nnever relative to a pre-iterated row. The\nplatform stores `null` here on fields driven by\n`hardCodedValue` instead of an extract path."},"generate":{"type":"string","description":"NetSuite sublist field ID to write to."},"hardCodedValue":{"type":"string","description":"Static value for this sublist field."},"lookupName":{"type":"string","description":"Reference to a lookup by name."},"dataType":{"type":"string","description":"Data type hint for the field value."},"internalId":{"type":"boolean","description":"When true, the value is a NetSuite internal ID reference."},"isKey":{"type":"boolean","description":"When true, this field is a key field for matching existing sublist lines."},"immutable":{"type":"boolean","description":"When true, this field is only set on record creation, not on updates."},"discardIfEmpty":{"type":"boolean","description":"When true, skips this field mapping if the extracted value is empty."},"extractDateFormat":{"type":"string","description":"Date format of the extracted value."},"extractDateTimezone":{"type":["string","null"],"description":"Timezone of the extracted date value. Stored as `null` when not configured."},"subRecordMapping":{"type":"object","description":"Nested Mapper 1.0 mapping for a NetSuite\nsubrecord embedded in this sublist-field.  A\nsubrecord is a child record that only exists\ninside a parent (e.g. the Inventory Detail on\na serialized-item line of a sales order);\nNetSuite reaches it through the parent.\n\n**Recursive structure.**  The nested\n`mapping` object has the same\n`{fields, lists}` shape as the top-level\n`netsuite_da.mapping`.  Recursion is allowed\nbut rare.","properties":{"recordType":{"type":"string","description":"NetSuite's internal record-type id of the\nSUBRECORD (not the parent).  E.g.\n`inventorydetail`, `itempricing`,\n`componentinventorydetail`.\n"},"jsonPath":{"type":"string","description":"JSONPath (relative to the current sublist\nline) to the rows that populate the\nsubrecord.  Typically `$` when the\nsubrecord data is carried on the same\nline item (the enclosing\n`lists[].jsonPath` has already selected\nthe line).  Defaults to `$`.\n"},"mapping":{"type":"object","description":"Nested `{fields, lists}` mapping for the\nsubrecord.  Identical shape to the\ntop-level `netsuite_da.mapping`.\nSubrecords often have empty `fields` and\nonly a sublist (e.g. `inventorydetail` →\n`inventoryassignment`).\n"},"lookups":{"type":"array","description":"Subrecord-scoped lookups.  Same shape as\nthe top-level `netsuite_da.lookups`.\nFields inside this\n`subRecordMapping.mapping` reference\nthem via `lookupName`.  Often `[]`.\n"},"referenceFieldId":{"type":"string","description":"Secondary reference-field identifier used\non a small fraction of subrecord blocks\nin production (~3%).  Exact semantics are\nnot publicly documented by Celigo — match\nthe value on existing resources when\npresent; do NOT synthesize one.  Leave\nunset unless a template clearly requires\nit.\n"}}},"conditional":{"type":"object","description":"Conditional logic for when to apply this sublist field mapping\n(\"Only perform mapping when\" in the UI). Lookup-based conditions\nevaluate the lookup named by `lookupName`, which must be defined\non this import.","properties":{"lookupName":{"type":"string","description":"Lookup to evaluate for the condition. Must exactly match the\n`name` of a lookup defined on this import."},"when":{"type":"string","description":"Condition that controls when this field mapping is applied.","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","ignore_if_set"]}}}}},"description":"Field mappings for each column in the sublist."}}},"description":"Sublist (line-item) mappings. Each entry maps source data to a NetSuite sublist.\n"}}},"lookups":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia `lookupName` and Handlebars expressions reference it via\n`{{lookup \"name\" value}}`. Must be unique within this\nresource's `lookups` array."},"recordType":{"type":"string","description":"NetSuite record type to search. Use the internal record-type\nid (lowercase, underscored), e.g. `customer`, `item`,\n`salesorder`, `customlist_xxx`."},"searchField":{"type":"string","description":"NetSuite field on `recordType` to match against. Use the\ninternal field id — e.g. `email`, `externalid`,\n`internalid`, `entityid`, `custitem_xxx`."},"resultField":{"type":"string","description":"NetSuite field whose value is returned when a record matches.\nTypically `internalid` when the lookup resolves to a record\nreference, or any queryable scalar field."},"expression":{"type":"string","description":"Optional advanced-search expression (NetSuite saved-search\nDSL) for multi-criterion lookups. When present, supersedes\n`searchField` / `operator` — those fields are ignored."},"operator":{"type":"string","description":"Comparison operator for the `searchField` match. Common\nvalues: `is`, `contains`, `startswith`, `anyof`,\n`equalto`, `greaterthan`."},"includeInactive":{"type":"boolean","description":"When true, includes inactive records in lookup results."},"useDefaultOnMultipleMatches":{"type":"boolean","description":"Controls behaviour when the search matches more than one\nrecord. When true, the `default` value is returned instead\nof raising an ambiguous-match error. When false (default),\nmultiple matches are treated as a failure."},"allowFailures":{"type":["boolean","null"],"description":"When true, a lookup miss (no matching record and no\n`default`) resolves to `null` and the import continues.\nWhen false (default), a miss halts the record. May be\nstored as null (treated as unset)."},"map":{"type":["object","null"],"description":"Optional static key→value object evaluated BEFORE the\nNetSuite search. When the incoming value matches a key, the\nmapped value is returned without hitting NetSuite. Null on\ndynamic (search-based) lookups with no static map."},"default":{"type":["string","null"],"description":"Value returned when the search matches no records. When\nomitted and `allowFailures` is false, an unmatched lookup\nhalts the record. May be stored as null (no default)."}}},"description":"Lookup definitions that resolve reference values from NetSuite.\nReferenced by name from field mappings via `lookupName` and from\nHandlebars expressions via `{{lookup \"name\" value}}`."},"rawOverride":{"type":"object","description":"Raw override object for advanced use cases. When useRawOverride is true,\nthis object is sent directly to the NetSuite API, bypassing normal mapping.\n"},"useRawOverride":{"type":"boolean","description":"When true, uses rawOverride instead of the normal mapping configuration.\n"},"isMigrated":{"type":"boolean","description":"When true, this import was migrated from a legacy format. Set by the system."},"retryUpdateAsAdd":{"type":"boolean","description":"When true, if an update fails because the record doesn't exist, automatically\nretry as an add operation. Useful for initial syncs where records may not exist yet.\n"},"customFieldMetadata":{"type":"object","description":"Metadata about custom fields on the target record type.\nPopulated by the system from NetSuite metadata — do not set manually.\n"},"file":{"type":"object","description":"File cabinet configuration for file-based imports into NetSuite.\n","properties":{"name":{"type":"string","description":"Filename for the file in NetSuite File Cabinet."},"fileType":{"type":"string","description":"NetSuite file type (e.g., \"PDF\", \"CSV\", \"PLAINTEXT\", \"EXCEL\", \"XML\").\n"},"folder":{"type":"string","description":"Folder path or name in the NetSuite File Cabinet."},"folderInternalId":{"type":"string","description":"Internal ID of the target folder in NetSuite File Cabinet."},"internalId":{"type":"string","description":"Internal ID of an existing file to update."},"backupFolderInternalId":{"type":"string","description":"Internal ID of a backup folder for file versioning."}}},"isFileProvider":{"type":"boolean","description":"Whether this import handles files in the NetSuite File Cabinet.\n"},"preferences":{"type":"object","properties":{"ignoreReadOnlyFields":{"type":"boolean","description":"When true, silently skip read-only fields instead of raising errors."},"warningAsError":{"type":"boolean","description":"When true, treat NetSuite warnings as errors that stop the import."},"skipCustomMetadataRequests":{"type":"boolean","description":"When true, skip fetching custom field metadata to improve performance."}},"description":"Import behavior preferences that control how NetSuite handles the import operation.\n"},"recordTypeId":{"type":"string","description":"The internal record type ID. Used for custom record types in NetSuite where\nthe numeric ID is needed in addition to the recordType string.\n"}},"if":{"not":{"propertyNames":{"enum":["lookups","mapping","missingOrCorruptedDAConfig"]}}},"then":{"required":["operation","recordType"],"if":{"properties":{"operation":{"enum":["update","addupdate","delete"]}},"required":["operation"]},"then":{"required":["internalIdLookup"]}}},"Rdbms":{"type":"object","description":"Configuration for RDBMS imports into SQL Server, MySQL, PostgreSQL, Snowflake, Oracle, MariaDB, and other relational databases.\nqueryType determines which companion field is required: per_record, per_page, and first_page use the query field; bulk_insert uses the bulkInsert object; bulk_load uses the bulkLoad object — never set query and bulkInsert together.\n(The server itself stores an empty `query: []` next to bulk_insert/bulk_load configs and may leave an empty `bulkInsert: {}` stub next to others — empty companions are normal in responses; only a populated conflicting companion is invalid.)\nThe query field is an array of plain SQL strings, not a single string and not an array of objects.","properties":{"lookups":{"$ref":"#/components/schemas/Lookups","description":"Lookup definitions executed against the RDBMS connection. Each\nentry runs a `SELECT` and extracts a value used by field mappings\n(referenced via `lookupName`) or Handlebars expressions\n(referenced via `{{lookup \"name\" value}}`).\n\n**Mirror invariant**\nThe Celigo platform persists lookups in TWO locations on an RDBMS\nimport: top-level `lookups` and `rdbms.lookups`. Both arrays\nmust hold byte-for-byte identical content — the MappingsAgent\nwrites both on every save.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia `lookupName` and Handlebars expressions reference it via\n`{{lookup \"name\" value}}`. Must be unique within this\nresource's `lookups` array.\n"},"query":{"type":"string","description":"SQL `SELECT` statement that resolves the lookup value.\nTypically returns a single row with a single column; use\n`extract` to pick the column when the query returns multiple.\nInject values from the incoming record with Handlebars — RDBMS\nqueries use triple braces and the `record.` prefix:\n`SELECT id FROM users WHERE email = '{{{record.email}}}'`.\n"},"extract":{"type":"string","description":"JSONPath-style expression that selects the value from the query\nresult row (e.g. `id`, `details.price`). Omit when the\nquery returns a single scalar — the first column is used\nautomatically.\n"},"_lookupCacheId":{"type":"string","format":"objectId","description":"Optional reference to a LookupCache resource. When set, the\nlookup reads from the cache instead of issuing a live SQL\nquery — useful for stable reference data that should be\npre-loaded.\n"},"map":{"type":"object","description":"Optional static key→value object evaluated BEFORE the SQL query.\nWhen the incoming value matches a key, the mapped value is\nreturned without querying the database. Useful for small\ncontrolled enumerations.\n"},"default":{"type":"string","description":"Value returned when the query matches no rows. When omitted and\n`allowFailures` is false, an unmatched lookup halts the\nrecord.\n"},"allowFailures":{"type":"boolean","description":"When true, a lookup miss (no matching row and no `default`)\nresolves to `null` and the import continues. When false\n(default), a miss halts the record.\n"}}},"type":"array"},"query":{"type":"array","items":{"type":"string"},"description":"SQL statements to execute; required when queryType is `[\"per_record\"]`, `[\"per_page\"]`, or `[\"first_page\"]`, and typically contains a single statement.\nEach element is a complete SQL statement as a plain string — never a single bare string and never an array of objects (objects cause a Cast error).\nInject values from the incoming record with Handlebars using the `record.` prefix and triple braces, e.g. `'{{{record.name}}}'` for strings (single-quoted in the SQL) and `{{{record.quantity}}}` for numbers.\nRendering is strict: a reference the render model cannot resolve (a bare `{{name}}` without prefix, or `record.*` when the arriving record is a grouped array) fails the step with `cannot_evaluate_handlebars` — it does not render an empty value.\n`record` and `rows` are aliases chosen by the arriving data's shape: an object record binds `record` (`rows` undefined); a grouped array-record (e.g. upstream `groupByFields`) binds `rows` (`record` undefined) — address grouped rows as `{{rows.0.field}}` or iterate `{{#each rows}}`.\nDouble braces (`{{record.field}}`) output the value wrapped in single quotes with embedded quotes doubled, while triple braces output it raw; block helpers such as `{{#each}}` and `{{#if}}` use double braces as normal."},"queryType":{"type":["array","null"],"items":{"type":"string","enum":["INSERT","UPDATE","bulk_insert","per_record","per_page","first_page","bulk_load"]},"description":"Execution strategy for the SQL operation, which determines the required companion field: per_record, per_page, and first_page require `query`; bulk_insert requires `bulkInsert`; bulk_load requires `bulkLoad`.\nPrefer the highest-performance type the operation supports: bulk_load (currently Snowflake and NSAW — handles INSERT, upsert via `bulkLoad.primaryKeys`, and custom merge or ignore-existing logic via `bulkLoad.overrideMergeQuery`), then bulk_insert for pure INSERTs, then per_page, then per_record only when the logic cannot be expressed as a batch operation.\nFor UPDATE, UPSERT, or ignore-existing logic on databases without bulk_load support, use per_page or per_record with `query` — bulk_insert has no duplicate-checking logic.\nSet a single value; do not combine types or use the legacy INSERT and UPDATE values.\nper_page statements render against the whole batch (`batch_of_records`) rather than a single record — loop with `{{#each batch_of_records}}...{{{record.fieldName}}}...{{/each}}`.\n\n`bulk_load` — Stages data as a file and loads via database-native COPY/bulk mechanism for maximum throughput; supports INSERT, upsert via bulkLoad.primaryKeys, and custom merge or ignore-existing logic via bulkLoad.overrideMergeQuery; currently supported for Snowflake and NSAW.\n`bulk_insert` — Batch INSERT via multi-row VALUES clause for efficient bulk data loading; has no upsert or ignore-existing logic and is available for all RDBMS types.\n`per_page` — Executes one SQL statement per page (batch) of records; available for all RDBMS types.\n`per_record` — Executes one SQL statement per record, giving full control over individual record SQL; available for all RDBMS types."},"bulkInsert":{"type":"object","description":"Configuration for batch INSERTs via a multi-row VALUES clause; required when queryType is `[\"bulk_insert\"]` and must not be set otherwise.\nOnly suited to pure INSERTs — for UPDATE, UPSERT, or ignore-existing/skip-duplicates logic, use queryType `[\"per_record\"]` with the `query` field instead.","properties":{"tableName":{"type":"string","description":"The name of the database table into which the bulk insert operation will be executed. This value must correspond to a valid, existing table within the target relational database management system (RDBMS). It serves as the primary destination for inserting multiple rows of data efficiently in a single operation. The table name can include schema or namespace qualifiers if supported by the database (e.g., \"schemaName.tableName\"), allowing precise targeting within complex database structures. Proper validation and sanitization of this value are essential to ensure the operation's success and to prevent SQL injection or other security vulnerabilities."},"batchSize":{"type":"string","description":"The number of records to be inserted into the database in a single batch during a bulk insert operation. This parameter is crucial for optimizing the performance and efficiency of bulk data loading by controlling how many records are grouped together before being sent to the database. Proper tuning of batchSize balances memory consumption, transaction overhead, and throughput, enabling the system to handle large volumes of data efficiently without overwhelming resources or causing timeouts. Adjusting batchSize directly impacts transaction size, network utilization, error handling granularity, and recovery strategies, making it essential to tailor this value based on the specific database capabilities, system resources, and workload characteristics."}}},"bulkLoad":{"type":"object","properties":{"tableName":{"type":"string","description":"Target table for the bulk load.\nMust reference an existing table accessible with the connection's credentials; schema-qualified names are supported."},"primaryKeys":{"type":"array","items":{"type":"string"},"description":"Columns that uniquely identify each record in the target table, used to match existing rows during the load.\nSet for upsert behavior (a MERGE is auto-generated); omit (or empty) for a pure INSERT.\nComposite keys are supported — list every key column, and names must exactly match the target schema."},"overrideMergeQuery":{"type":"boolean","description":"When true, a custom merge query replaces the auto-generated MERGE statement, enabling ignore-existing logic, conditional updates, or multi-table operations.\nThe override SQL references `{{import.rdbms.bulkLoad.preMergeTemporaryTable}}` for the staging table."}},"description":"Configuration for bulk loading: data is staged as a file and loaded via the database's native COPY/bulk mechanism for maximum throughput.\nRequired when queryType is `[\"bulk_load\"]`; currently supported for Snowflake and NSAW."},"updateLookupName":{"type":["string","null"],"description":"Name of the lookup used for update operations in the legacy composite (queryType1) flow."},"updateExtract":{"type":["string","null"],"description":"Path used to extract the value that drives update operations in the legacy composite flow."},"ignoreLookupName":{"type":["string","null"],"description":"Name of the lookup used to determine which records to ignore when ignore-existing is enabled."},"ignoreExtract":{"type":["string","null"],"description":"Path used to extract the value that determines whether a record is ignored when ignore-existing is enabled."}},"if":{"not":{"propertyNames":{"enum":["lookups"]}}},"then":{"required":["queryType"],"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"enum":["per_record","per_page"]}}}},"then":{"required":["query"],"properties":{"bulkInsert":{"maxProperties":0}}},"else":{"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"const":"first_page"}}}},"then":{"required":["query"]},"else":{"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"const":"bulk_insert"}}}},"then":{"required":["bulkInsert"],"properties":{"query":{"maxItems":0}}},"else":{"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"const":"bulk_load"}}}},"then":{"required":["bulkLoad"],"properties":{"query":{"maxItems":0}}}}}}}},"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 set to true\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\nIf allowFailures is false and no default is specified, the flow will fail\nwhen encountering unmapped values.\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\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\nThe best practice is typically to set allowFailures to true with a meaningful\ndefault value, so flows remain operational while alerting you to missing mappings.\n"}}}},"S3":{"type":"object","description":"Defines where files are written to 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 destination, fileKey sets the object key, and backupBucket\nretains a copy after a successful import.","required":["region","bucket"],"properties":{"region":{"type":"string","default":"us-east-1","description":"AWS region where the S3 bucket resides. Must match the bucket's actual location to avoid\nconnectivity errors and misrouted requests."},"bucket":{"type":"string","description":"S3 bucket that receives the imported files. The bucket must already exist and the\nconnection's AWS credentials must have s3:PutObject permission on it."},"fileKey":{"type":"string","description":"Object key under which each file is stored in the bucket. Include slashes to organize\nfiles in a folder-style hierarchy (for example, imports/2023/12/orders.json); keys are\ncase-sensitive. Supports handlebars placeholders such as orders-{{timestamp}}.json to\ngenerate a unique key per run."},"backupBucket":{"type":"string","description":"S3 bucket where a copy of each imported file is retained after a successful import; if\nomitted, no backup copy is kept. Must reference an existing bucket the connection's\ncredentials have s3:PutObject permission on."},"serverSideEncryptionType":{"type":"string","description":"Server-side encryption applied to uploaded objects. The connection form sets `AES256`\n(SSE-S3, S3-managed keys) when encryption is enabled; leave unset to use the bucket's\ndefault encryption."}}},"Wrapper":{"type":"object","description":"Configuration for Wrapper imports, which delegate writing records 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.","properties":{"function":{"type":"string","description":"Name of the function the wrapper invokes to process records.\nMust match a callable function in the wrapper's execution context; names are case-sensitive."},"configuration":{"type":"object","additionalProperties":true,"description":"Free-form settings passed to the wrapper function at runtime (connector-specific keys such\nas method, apiVersion, headers, or handler). Structure is defined by the wrapper code, not\nby this schema."},"lookups":{"$ref":"#/components/schemas/Lookups","description":"Lookup definitions for this wrapper import. Wrapper lookups use\nthe generic Celigo shape: either a static `map` (key→value\nobject) or a dynamic `_lookupCacheId` + `extract` pair. See\n`common/schemas/lookups.yml` for the full item shape."}},"if":{"not":{"propertyNames":{"enum":["lookups"]}}},"then":{"required":["function"]}},"Salesforce":{"type":"object","description":"Salesforce-specific configuration for the import: the operation to perform, the API to use, and object-level settings such as `sObjectType`, `idLookup`, and `upsert`. Properties like `ignoreExisting`, `ignoreMissing`, `name`, and `description` are not part of this object — they belong at the import level.","properties":{"lookups":{"description":"Lookup definitions used to resolve Salesforce record references or\nstatic value mappings. Each lookup has a unique ``name`` that mapping\nfields reference via ``lookupName`` and that Handlebars expressions\nreference via ``{{lookup \"name\" value}}``.\n\n**Mirror invariant**\nThe Celigo platform stores lookups in TWO locations on a Salesforce\nimport resource: top-level ``lookups`` and ``salesforce.lookups``.\nBoth arrays must hold byte-for-byte identical content — the\nMappingsAgent writes both on every save.\n\n**Static vs dynamic lookups**\n- **Static** — only ``map`` + optional ``default``. No Salesforce\n  query runs. Use for small controlled vocabularies (country codes,\n  status labels, etc.).\n- **Dynamic** — ``sObjectType`` + ``resultField`` + ``whereClause``.\n  Issues a SOQL query at runtime. Use ``map`` alongside the query to\n  short-circuit known values before hitting Salesforce.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup entry. Mapping fields reference\nit via ``lookupName``, and Handlebars expressions reference it\nvia ``{{lookup \"name\" value}}``. Must be unique within this\nresource's ``lookups`` array. Conventionally a short slug or a\ngenerated hash — the value is only meaningful as a reference\ntoken.\n"},"sObjectType":{"type":"string","description":"Salesforce sObject API name to query (e.g. ``Account``,\n``Contact``, ``Solution__c``). For custom objects include the\n``__c`` suffix. Case-sensitive — must match exactly as declared\nin the target Salesforce org.\n"},"resultField":{"type":"string","description":"sObject field whose value is returned when the SOQL ``whereClause``\nmatches a record. Typically ``Id`` when the lookup resolves to a\nSalesforce record reference, but any queryable field is valid.\n"},"whereClause":{"type":"string","description":"SOQL ``WHERE`` clause that identifies the matching Salesforce\nrecord. Use Handlebars expressions (triple braces recommended:\n``{{{field}}}``) to inject values from the incoming source record.\nWrap the whole expression in parentheses.\n\nExamples:\n- ``(Id = {{{id [Customer SFDC ID]}}})``\n- ``(Email = '{{{email}}}')``\n- ``(External_Id__c = {{{string externalId}}} AND IsActive = true)``\n\nCeligo's Handlebars helpers (``string``, ``id``, ``double``,\n``bool``, ``date``) produce correctly-typed SOQL literals —\nprefer them over manual quoting. String values wrap in single\nquotes; numeric/id values do not.\n"},"map":{"type":["object","null"],"description":"Optional static key→value map evaluated BEFORE the SOQL query.\nWhen the incoming field value matches a key, the mapped value\nis returned without hitting Salesforce. Useful for small\ncontrolled enumerations where a full query would be overkill.\n"},"default":{"type":["string","null"],"description":"Value returned when the SOQL query matches no records. When\nomitted and ``allowFailures`` is false, an unmatched lookup\nfails the record.\n"},"allowFailures":{"type":"boolean","description":"When true, a lookup miss (no matching record and no ``default``)\nis tolerated — the mapped field resolves to ``null`` and the\nimport continues. When false (default), a miss halts processing\nfor the record.\n"},"useDefaultOnMultipleMatches":{"type":"boolean","description":"Controls behaviour when the SOQL query matches more than one\nrecord. When true, the ``default`` value is returned instead of\nraising an ambiguous-match error. When false (default), multiple\nmatches are treated as a failure.\n"}}},"type":"array"},"operation":{"type":"string","enum":["insert","update","upsert","upsertpicklistvalues","delete","addupdate"],"description":"Controls how records are written to Salesforce. Each value pairs with a lookup strategy: `upsert` requires `upsert.externalIdField` plus `idLookup.extract`, while `update`, `delete`, `addupdate`, and `insert` with `ignoreExisting` require `idLookup.whereClause`. Values are converted to lowercase automatically."},"api":{"type":"string","enum":["soap","rest","metadata","compositerecord"],"description":"Selects the Salesforce API used to execute the import. Use `soap` for most imports and another API only when its specific capability is needed; default to `soap` when no API is specified. Values are converted to lowercase automatically."},"soap":{"type":"object","properties":{"headers":{"type":"object","properties":{"allOrNone":{"type":"boolean","description":"When true, the batch is processed atomically: if any record fails, the entire transaction is rolled back and nothing is committed. When false or omitted, records that succeed are committed even if others in the batch fail."}},"description":"SOAP headers included in requests sent to Salesforce, such as the `allOrNone` transaction-control header."},"batchSize":{"type":"number","description":"Number of records sent to Salesforce in each SOAP API batch. Larger batches reduce the number of API calls; smaller batches reduce per-call load and the chance of timeouts."}},"description":"Settings that apply when `api` is `soap`, including SOAP request headers and batch size."},"sObjectType":{"type":"string","description":"Salesforce object the import writes to, such as `Account`, `Contact`, or a custom object like `Vendor__c`. Must exactly match the object's case-sensitive API name; custom objects include the `__c` suffix."},"idLookup":{"type":"object","properties":{"extract":{"type":"string","description":"Field in the incoming data whose value is matched against the Salesforce External ID field named in `upsert.externalIdField`. Set only when `operation` is `upsert`; all other operations use `whereClause` instead. Choose a field that is unique and always populated — missing or null values cause the upsert to fail. If both `extract` and `whereClause` are set, `extract` takes precedence for upsert."},"whereClause":{"type":"string","description":"SOQL condition (without the `WHERE` keyword) that finds existing Salesforce records for each incoming record. Required when `operation` is `update`, `delete`, or `addupdate`, and for `insert` with `ignoreExisting`; for `upsert`, use `extract` instead. Reference incoming fields with triple-brace Handlebars and wrap string values in single quotes (e.g. `Email = '{{{Email}}}'`) — triple braces emit the raw value without HTML-escaping, which would corrupt characters like `&` or `'`. Combine conditions with `AND`/`OR`."}},"description":"Controls how existing Salesforce records are matched before writing. Set exactly one of `extract` (for `upsert`) or `whereClause` (for `update`, `delete`, `addupdate`, and `insert` with `ignoreExisting`); omit for plain inserts.\n\nRequired when operation is addupdate, delete, update, or upsert."},"upsert":{"type":"object","properties":{"externalIdField":{"type":"string","description":"API name of the Salesforce field, marked as External ID, that upsert operations match against. This is the target side of the match; `idLookup.extract` names the source field in the incoming data. If the value matches multiple Salesforce records, the upsert fails."}},"description":"Upsert matching configuration, required when `operation` is `upsert` and omitted for all other operations. Contains only `externalIdField`, which pairs with `idLookup.extract` at the parent level.\n\nRequired when operation is upsert."},"upsertpicklistvalues":{"type":"object","properties":{"type":{"type":"string","enum":["picklist","multipicklist"],"description":"Picklist field type being managed. Values are converted to lowercase automatically."},"fullName":{"type":"string","description":"Fully qualified API name of the picklist field, in `ObjectName.FieldName__c` form (for example, `Account.MyPicklist__c`). Must match the case-sensitive Salesforce API name."},"label":{"type":"string","description":"Display label for the picklist field in the Salesforce UI. Can contain spaces and does not need to match the API name."},"visibleLines":{"type":"number","description":"Number of values visible without scrolling in the Salesforce UI selection box. Only applies when `type` is `multipicklist`."}},"description":"Picklist field definition managed when `operation` is `upsertpicklistvalues`; omit for all other operations. Manages the field's metadata — name, label, and display settings — and requires Salesforce Metadata API permissions.\n\nRequired when operation is upsertpicklistvalues."},"removeNonSubmittableFields":{"type":"boolean","description":"When true, strips fields Salesforce will not accept — read-only, system-generated, and formula fields — from the payload before submission, preventing rejection errors. When false or omitted, all fields are submitted as-is. Only the outgoing payload is affected; the source data is unchanged."},"document":{"type":"object","properties":{"id":{"type":"string","description":"Salesforce-assigned ID of the Document record. The 15-character form is case-sensitive; the 18-character form adds a case-insensitivity checksum."},"name":{"type":"string","description":"Display name of the document in Salesforce, used to identify it in folders, search, and API operations."},"folderId":{"type":"string","description":"Salesforce folder that stores the document. The document inherits the folder's sharing and visibility settings; changing this value moves the document to a different folder."},"contentType":{"type":"string","description":"MIME type of the document content, such as `application/pdf` or `image/png`."},"developerName":{"type":"string","description":"Unique API name used to reference the document in code, metadata, and integrations. Distinct from the display name; uses only letters, numbers, and underscores with no spaces."},"isInternalUseOnly":{"type":"boolean","description":"When true, marks the document for internal use only — it must not be shared with parties outside the organization."},"isPublic":{"type":"boolean","description":"When true, the document is accessible to all users; when false, access is limited to authorized users by Salesforce permissions and sharing settings."}},"description":"Salesforce Document record written by the import, combining the file content with metadata such as name, folder, and content type."},"attachment":{"type":"object","properties":{"id":{"type":"string","description":"Salesforce-assigned ID of the Attachment record. Prefer the 18-character case-insensitive form over the 15-character case-sensitive form in integrations."},"name":{"type":"string","description":"Filename of the attachment, including the file extension (for example, `contract_agreement.pdf`)."},"parentId":{"type":"string","description":"Salesforce record the attachment is linked to, such as an Account, Contact, or Opportunity. Required when creating an attachment; access to the attachment follows the parent record's permissions."},"contentType":{"type":"string","description":"MIME type of the attachment content, such as `application/pdf` or `image/png`."},"isPrivate":{"type":"boolean","description":"When true, the attachment is visible only to users explicitly authorized to access it; when false or omitted, it inherits the visibility of its parent record. This setting only restricts access further — it never grants access beyond the parent record's permissions."},"description":{"type":"string","description":"Free-text note describing the attachment's content or purpose, shown alongside the attachment details in Salesforce."}},"description":"Salesforce Attachment record written by the import — a file linked to a parent record via `parentId`, with metadata such as name and content type."},"contentVersion":{"type":"object","properties":{"contentDocumentId":{"type":"string","description":"ContentDocument this version belongs to; all versions of the same document share this ID. Assigned by Salesforce when the content version is created and immutable afterward."},"title":{"type":"string","description":"Display title of this content version, shown in content libraries, search results, and version history."},"pathOnClient":{"type":"string","description":"Original file path on the client machine before upload. Used for reference and display only; it does not affect how the file is stored or accessed in Salesforce."},"tagCsv":{"type":"string","description":"Comma-separated tags applied to the content version for categorization and search. Updating this field replaces the entire tag set, so include every tag the version should keep."},"contentLocation":{"type":"string","description":"Specifies where the content file for this ContentVersion record is stored — \"S\" for Salesforce internal storage or \"E\" for an external system (the two values the import form offers). Salesforce also defines \"L\" for content shared via a link, but the form does not set it. Typically assigned automatically based on the upload method."}},"description":"Salesforce ContentVersion record written by the import — a specific version of a content item in Salesforce Files. Each modification produces a new version, and all versions of a document are linked through `contentDocumentId`."}},"if":{"anyOf":[{"required":["attachment"]},{"required":["contentVersion"]}]},"then":{"required":["operation"]},"else":{"if":{"not":{"propertyNames":{"enum":["lookups","api"]}}},"then":{"required":["operation","api"],"if":{"properties":{"operation":{"const":"upsert"}},"required":["operation"]},"then":{"required":["idLookup","upsert"],"properties":{"idLookup":{"required":["extract"]},"upsert":{"required":["externalIdField"]}}},"else":{"if":{"properties":{"operation":{"enum":["update","delete","addupdate"]}},"required":["operation"]},"then":{"required":["idLookup"],"properties":{"idLookup":{"required":["whereClause"]}}},"else":{"if":{"properties":{"operation":{"const":"upsertpicklistvalues"}},"required":["operation"]},"then":{"required":["upsertpicklistvalues"],"properties":{"upsertpicklistvalues":{"required":["fullName"]}}}}}}}},"Tool":{"type":"object","description":"Configuration for a `ToolImport`, which executes a reusable Celigo Tool resource as the\nimport action. The referenced tool defines its own input contract, processing pipeline, and\noutput; this object binds the tool to the import and optionally overrides the connections the\ntool uses for this invocation.","required":["_toolId"],"properties":{"_toolId":{"type":"string","format":"objectId","description":"Tool resource this import executes. The tool supplies the input schema, routing, page\nprocessors, and output mapping; the import runs that tool when records reach it. Required —\na `ToolImport` has no behavior without a tool to run."},"overrides":{"type":"object","description":"Per-import overrides applied to the referenced tool at execution time. Tools use a\nbring-your-own-keys connection model: the tool declares abstract connection slots, and the\nconsumer binds concrete connections here so one tool definition can run against different\nsystems without being modified. Omit to run the tool with its default connections.","properties":{"connections":{"type":"array","description":"Connection bindings that replace the tool's abstract connection slots for this import.\nEach entry maps one of the tool's abstract connections to the concrete connection the\nimport should use.","items":{"type":"object","required":["_id","_abstractId"],"properties":{"_id":{"type":"string","format":"objectId","description":"Concrete connection bound to the abstract slot for this import's tool run."},"_abstractId":{"type":"string","format":"objectId","description":"Abstract connection slot, declared by the tool, that this binding replaces. Matches\nthe abstract connection id defined on the referenced tool."},"_borrowConcurrencyFromConnectionId":{"type":"string","format":"objectId","description":"Optional pointer to another connection whose\nconcurrency budget this binding shares. Use\nwhen several wrappers / steps should share rate\nlimits against the same downstream system but\nuse different credentials. Mutually exclusive\nwith `_id` on the same entry.\n"}}}}}}}},"File":{"type":"object","description":"**CRITICAL: This object is REQUIRED for all file-based import adaptor types.**\n\n**When to include this object**\n\n✅ **MUST SET** when `adaptorType` is one of:\n- `S3Import`\n- `FTPImport`\n- `AS2Import`\n- `HTTPImport` in file mode (`http.type: \"file\"` — the folder mode of the cloud file\n  providers such as Google Drive, Box, Dropbox, Azure Blob, Google Cloud Storage and Celigo\n  Storage); the same `file` object, including `type: filedefinition` with `_ediProfileId`\n  for EDI, applies as on S3 / FTP\n\n❌ **DO NOT SET** for non-file-based imports like:\n- `SalesforceImport`\n- `NetSuiteImport`\n- `HTTPImport` in records mode (`http.type` unset — a REST endpoint receiving records)\n- `MongodbImport`\n- `RDBMSImport`\n\n**Minimum required fields**\n\nFor most file imports, you need at minimum:\n- `fileName`: The output file name (supports Handlebars like `{{timestamp}}`)\n- `type`: The file format (json, csv, xml, xlsx)\n- `aggregation.type`: How the run's records are batched into files (`all` for one file per run)\n\n**Example (S3 Import)**\n\n```json\n{\n  \"file\": {\n    \"fileName\": \"customers-{{timestamp}}.json\",\n    \"aggregation\": { \"type\": \"all\" },\n    \"type\": \"json\"\n  },\n  \"s3\": {\n    \"region\": \"us-east-1\",\n    \"bucket\": \"my-bucket\",\n    \"fileKey\": \"customers-{{timestamp}}.json\"\n  },\n  \"adaptorType\": \"S3Import\"\n}\n```","properties":{"fileName":{"type":"string","description":"**REQUIRED for file-based imports.**\n\nThe name of the file to be created/written. Supports Handlebars expressions for dynamic naming.\n\n**Default behavior**\n- When the user does not specify a file naming convention, **always default to timestamped filenames** using `{{timestamp}}` (e.g., `\"items-{{timestamp}}.csv\"`). This ensures each run produces a unique file and avoids overwriting previous exports.\n- Only use a fixed filename (without timestamp) if the user explicitly requests overwriting or a fixed name.\n\n**Common patterns**\n- `\"data-{{timestamp}}.json\"` - Timestamped JSON file (DEFAULT — use this pattern when not specified)\n- `\"export-{{date}}.csv\"` - Date-stamped CSV file\n- `\"{{recordType}}-backup.xml\"` - Dynamic record type naming\n\n**Examples**\n- `\"customers-{{timestamp}}.json\"`\n- `\"orders-export.csv\"`\n- `\"inventory-{{date}}.xlsx\"`\n\n**Important**\n- For S3 imports, this should typically match the `s3.fileKey` value\n- For FTP imports, this should typically match the `ftp.fileName` value"},"skipAggregation":{"type":"boolean","description":"Legacy companion of `aggregation.type`: `true` when the run's records are written one\nfile per page, `false` when they are combined across the run or grouped by key.\nValidation reads the value sent — an EDI file definition needs `true` unless\n`aggregation.type` is `key`, and `batchSize` needs `true` — then the save rewrites it\nfrom `aggregation.type`, so send the two as a consistent pair.","default":false},"type":{"type":"string","enum":["json","csv","xml","xlsx","filedefinition"],"description":"**REQUIRED for file-based imports.**\n\nThe format of the output file.\n\nSelect the format that matches the target system's requirements."},"encoding":{"type":"string","enum":["utf8","win1252","utf-16le","gb18030","macroman","iso88591","shiftjis"],"description":"Character encoding for the output file.\n\n**Default:** `\"utf8\"`\n\nChange from the default only when the target system requires a specific encoding.","default":"utf8"},"delete":{"type":"boolean","description":"Whether to delete the source file after successful import.\n\n**Values**\n- `true`: Delete source file after processing\n- `false`: Keep source file\n\n**Default:** `false`","default":false},"compressionFormat":{"type":"string","enum":["gzip","zip"],"description":"Compression format for the output file.\n\n**When to use**\n- Large files that benefit from compression\n- When target system expects compressed files"},"backupPath":{"type":"string","description":"Path where backup copies of files should be stored.\n\n**Examples**\n- `\"backup/\"` - Relative backup folder\n- `\"/archive/2024/\"` - Absolute backup path"},"purgeInternalBackup":{"type":"boolean","description":"Whether to purge internal backup copies after successful processing.\n\n**Default:** `false`","default":false},"batchSize":{"type":"integer","description":"Number of records to include per batch/file when processing large datasets.\n\n**When to use**\n- Large imports that need to be split into multiple files\n- When target system has file size limitations\n\n**Note**\n- Offered when records are written one file per page (`aggregation.type: page` with\n  `skipAggregation: true`)"},"encrypt":{"type":"boolean","description":"Whether to encrypt the output file.\n\n**Values**\n- `true`: Encrypt the file (requires PGP configuration)\n- `false`: No encryption\n\n**Default:** `false`","default":false},"csv":{"type":"object","description":"CSV-specific configuration. Only used when `type` is `\"csv\"`.","properties":{"rowDelimiter":{"type":"string","description":"Character(s) used to separate rows. Default is newline.","default":"\n"},"columnDelimiter":{"type":"string","description":"Character(s) used to separate columns. Default is comma.","default":","},"includeHeader":{"type":"boolean","description":"When true, includes a header row with column names in the output.","default":true},"wrapWithQuotes":{"type":"boolean","description":"When true, wraps field values in quotes.","default":false},"replaceTabWithSpace":{"type":"boolean","description":"Replace tab characters with spaces.","default":false},"replaceNewlineWithSpace":{"type":"boolean","description":"Replace newline characters with spaces within fields.","default":false},"truncateLastRowDelimiter":{"type":"boolean","description":"Remove trailing row delimiter from the file.","default":false}}},"json":{"type":"object","description":"JSON-specific configuration. Only used when `type` is `\"json\"`.","properties":{"resourcePath":{"type":"string","description":"JSONPath expression to locate records within the JSON structure."}}},"xml":{"type":"object","description":"XML-specific configuration. Only used when `type` is `\"xml\"`.","properties":{"resourcePath":{"type":"string","description":"XPath expression to locate records within the XML structure."}}},"fileDefinition":{"type":"object","description":"Configuration settings for parsing files using a predefined file definition. This object enables processing of complex, non-standard, or proprietary file formats that require specialized parsing logic beyond what the standard parsers (CSV, JSON, XML, etc.) can handle.\n\n**When to use**\n\nConfigure this object when the `type` field is set to \"filedefinition\". This approach is required for properly handling:\n- Legacy or proprietary file formats with complex structures\n- Fixed-width text files where field positions are defined by character positions\n- Electronic Data Interchange (EDI) documents (X12, EDIFACT, etc.)\n- Multi-record type files where different lines have different formats\n- Files requiring complex preprocessing or custom parsing logic\n\n**File definition characteristics**\n\n- **Custom Parsing Rules**: Applies predefined parsing logic to complex file formats\n- **Reusable Configurations**: References externally defined parsing rules that can be reused\n- **Complex Format Support**: Handles formats that standard parsers cannot process\n- **Specialized Processing**: Often used for industry-specific or legacy formats\n\n**Implementation strategy for ai agents**\n\n1. **Format Analysis**:\n    - Determine if the file format is standard (CSV, JSON, XML) or requires custom parsing\n    - Check if the format follows industry standards like EDI, SWIFT, or fixed-width\n    - Assess if there are multiple record types within the same file\n    - Identify if specialized logic is needed to interpret the file structure\n\n2. **File Definition Selection**:\n    - Verify that a suitable file definition has already been created in the system\n    - Check if existing file definitions match the format requirements\n    - Confirm the file definition ID from system administrators if needed\n    - Ensure the file definition is compatible with the export's needs\n","properties":{"_fileDefinitionId":{"type":"string","format":"objectId","description":"Reference to the file definition resource."}}},"aggregation":{"type":"object","description":"Controls how a flow run's records are batched into output files — the transfer form's\n\"How many files would you like to generate?\" setting. The legacy `skipAggregation` flag\nmirrors `type` and is maintained by the platform.","properties":{"type":{"type":"string","enum":["all","page","key"],"description":"Which batches of a run's records become separate output files. EDI file definitions\n(`file.type: filedefinition`) are written per page or per key, never per run; `key`\nis accepted only for them and only when the account has B2B Manager. The grouping\nkey and the line-item loop it merges come from the EDI file definition's document\ntype, so there is no separate key field to configure."}}},"lookups":{"type":"array","description":"Named value-substitution maps available to this import's field mappings; each lookup\ntranslates source values to target values, with an optional default for unmatched values.\nReferenced by name from mapping rules.","items":{"type":"object","properties":{"name":{"type":"string","description":"Identifier used to reference this lookup from mapping rules."},"map":{"type":"object","description":"Source-value to target-value pairs applied during mapping.","additionalProperties":{"type":"string"}},"default":{"type":["string","null"],"description":"Value substituted when a source value is not found in map; empty string when omitted. May be stored as null (no default)."},"allowFailures":{"type":"boolean","description":"When true, a value missing from map does not fail the record and the default (or original value) is used."}}}},"pgp":{"type":"object","description":"PGP encryption settings applied when encrypt is true; selects the symmetric cipher and the\nsigning hash. The connection must hold the recipient's public key.","properties":{"symmetricKeyAlgorithm":{"type":"string","enum":["twofish","cast5","3des","aes128","aes192","aes256"],"default":"aes256","description":"Symmetric cipher used to encrypt the file body; aes256 unless the recipient requires another."},"hashAlgorithm":{"type":"string","enum":["sha256","sha384","sha512","sha224"],"description":"Hash algorithm used when signing the encrypted file; set only when the recipient requires a specific one."}}},"skipRename":{"type":"boolean","description":"When true, the import writes directly to the final file name instead of writing to a\ntemporary name and renaming on completion. Use only when the destination does not support\natomic rename; the default (false) uses the safe write-then-rename behavior."},"directory":{"type":"object","required":["pathMode"],"description":"Structured destination location for cloud file-provider imports (Google Drive shared\ndrives, Box, Dropbox), replacing the flat path used by classic FTP/S3 destinations. Select\nthe location by folder ID or by a path relative to a configured storage root via pathMode.\nFor these cloud providers, directoryId mode addresses the folder by id alone — the flat\ndestination-path field is not consulted at runtime.","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 destination directory (e.g. a Google Drive folder ID);\nset when pathMode is directoryId. This is the file provider's own ID, not a Celigo resource ID."},"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."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root the relative path resolves against; set when\npathMode is relativePath. The provider's own ID, not a Celigo resource ID."},"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 location for backup copies on cloud file providers — the file-provider\ncounterpart 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."}}}}},"FileSystem":{"type":"object","description":"Defines where files are written to 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 where generated files are written; the agent's OS\naccount must have write permission on it. Accepts a local OS path or a UNC network share,\nand supports handlebars templates for dynamic folders."}}},"AiAgentConfig":{"type":"object","description":"AI Agent configuration used by both AiAgentImport and GuardrailImport (ai_agent type).\n\nConfigures which AI provider and model to use, along with instructions, parameter\ntuning, output format, and available tools. Providers come in two families with\ntwo configuration formats:\n\n- **Built-in providers** — each has its own configuration block.\n  - **openai**: OpenAI models (GPT-4.1, GPT-5, etc.). Configure via the `openai` object.\n  - **gemini**: Google Gemini models via the LiteLLM proxy. Configure via `litellm` with overrides in `litellm._overrides.gemini`.\n  - **anthropic**: Anthropic Claude models via the LiteLLM proxy. Configure via `litellm` with overrides in `litellm._overrides.anthropic`.\n- **Catalog providers** — `mistral`, `xai`, `huggingface`, `deepseek`, `cohere`, `groq`. Added\n  as catalog data rather than platform code, they share one generic flat format:\n  `model`, `modelOptions`, `instructions`, `output`, and the `tools` / `prompts` /\n  `resources` arrays directly on this object.\n\nA `_connectionId` on the parent import selects bring-your-own-key (BYOK)\ncredentials. Without one, built-in providers run on platform-managed\ncredentials; catalog providers have no platform-managed credentials, so they\nsave without a connection but cannot run until one is attached.\n","required":["provider"],"properties":{"provider":{"type":"string","enum":["openai","gemini","anthropic","mistral","xai","huggingface","deepseek","cohere","groq"],"description":"AI provider to use."},"model":{"type":"string","description":"Model identifier for a catalog provider (generic flat format) — for example\n`grok-4.6`. Built-in providers set the model inside their own block\n(`openai.model`, `litellm.model`) instead."},"modelOptions":{"type":"object","description":"Model tuning for a catalog provider (generic flat format). Which keys, values, and\nranges are accepted is defined per catalog model and validated on save when the\nmodel is a catalog entry.","properties":{"maxOutputTokens":{"type":"number","description":"Maximum number of tokens the model may generate."},"temperature":{"type":"number","description":"Sampling temperature."},"topP":{"type":"number","description":"Nucleus sampling threshold."},"reasoning_effort":{"type":"string","enum":["low","medium","high","xhigh"],"description":"Reasoning depth for models that expose it."}}},"instructions":{"type":"string","description":"System prompt for a catalog provider (generic flat format). Built-in providers\nset it inside their own block (`openai.instructions`,\n`litellm._overrides.anthropic.systemInstruction`)."},"output":{"type":"object","description":"Output format for a catalog provider (generic flat format). Built-in providers\nconfigure it inside their own block (`openai.output`, `litellm.responseFormat`).","properties":{"format":{"type":"object","description":"Controls the structure of the model's output.","properties":{"type":{"type":"string","enum":["text","json_schema"],"description":"Output type."},"name":{"type":"string","description":"Name of the JSON schema, for `json_schema` output."},"strict":{"type":"boolean","description":"When true, the model must conform exactly to `jsonSchema`."},"jsonSchema":{"type":"object","additionalProperties":true,"description":"JSON Schema the structured output must conform to, for `json_schema` output."}}},"verbose":{"type":"string","description":"Level of detail in the model's response, for models that expose it."}}},"tools":{"type":"array","description":"Tools available to a catalog-provider agent (generic flat format). Entries mirror\nthe built-in providers' tool entries: a Celigo Tool by `_toolId`, an MCP server by\n`_mcpConnectionId`, or a vendor web-search tool.","items":{"type":"object","properties":{"type":{"type":"string","enum":["tool","mcp","web_search"],"description":"Type of tool entry."},"tool":{"type":"object","description":"Reference to a Celigo Tool resource, used when type is \"tool\".","properties":{"_toolId":{"type":"string","format":"objectId","description":"The Celigo Tool to call."}}},"mcp":{"type":"object","description":"MCP server connection, used when type is \"mcp\".","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if omitted). Each entry\nis either a plain tool name or an object carrying display metadata.","items":{"type":["string","object"]}}}},"config":{"type":"object","additionalProperties":true,"description":"Tool-specific options for vendor-native tools (for example web-search settings)."}}}},"prompts":{"type":"array","description":"MCP prompt entries available to a catalog-provider agent (generic flat format).\nEach entry references one MCP connection and the prompt names allowed from it.","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server that exposes the prompts."},"allowedPrompts":{"type":"array","description":"Prompt names the agent may fetch from the server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"},"openai":{"type":"object","description":"OpenAI-specific configuration. Used when `provider` is \"openai\".\n","required":["model","instructions"],"properties":{"instructions":{"type":"string","maxLength":1000000,"description":"System prompt that defines the AI agent's behavior, goals, and constraints.\n"},"model":{"type":"string","description":"OpenAI model identifier. Open string (not an enum) — model names change frequently."},"reasoning":{"type":"object","description":"Controls depth of reasoning for complex tasks.","properties":{"effort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh"],"description":"How much reasoning effort the model should invest"},"summary":{"type":"string","enum":["concise","auto","detailed"],"description":"Level of detail in reasoning summaries"}}},"temperature":{"type":"number","minimum":0,"maximum":2,"description":"Sampling temperature. Higher values (e.g. 1.5) produce more creative output,\nlower values (e.g. 0.2) produce more focused and deterministic output.\n"},"topP":{"type":"number","minimum":0.1,"maximum":1,"description":"Nucleus sampling parameter"},"topLogprobs":{"type":"number","minimum":0,"maximum":20,"description":"Number of most likely tokens to return log probabilities for at each output position."},"maxOutputTokens":{"type":"number","minimum":100,"maximum":128000,"default":5000,"description":"Maximum number of tokens in the model's response (server default observed live on create)"},"serviceTier":{"type":"string","enum":["auto","default","priority"],"default":"default","description":"OpenAI service tier. \"priority\" provides higher rate limits and\nlower latency at increased cost."},"output":{"type":"object","description":"Output format configuration","properties":{"format":{"type":"object","description":"Controls the structure of the model's output.\n","properties":{"type":{"type":"string","enum":["text","json_schema","blob"],"default":"text","description":"Output format type."},"schemaMode":{"type":"string","enum":["manual","json"],"description":"How the structured-output schema was authored in the UI.\nEditor state only — it does not change how `jsonSchema` is\nsent to the provider."},"name":{"type":"string","description":"Name for the output format (used with json_schema)"},"strict":{"type":"boolean","default":false,"description":"When true, enforces strict schema validation on output."},"jsonSchema":{"type":"object","description":"JSON Schema for structured output. Required when `format.type` is \"json_schema\".\n","properties":{"type":{"type":"string","description":"Root JSON Schema type of the structured output; use \"object\" for record-shaped results.","enum":["object","array","string","number","integer","boolean"]},"properties":{"type":"object","additionalProperties":true,"description":"JSON Schema definitions for each field the structured output may contain."},"required":{"type":"array","description":"Property names the model must include in the structured output.","items":{"type":"string"}},"additionalproperties":{"type":"boolean","description":"When true, the structured output may include properties beyond those defined in `properties`."}}}},"if":{"properties":{"type":{"const":"json_schema"}},"required":["type"]},"then":{"required":["name","jsonSchema"]}},"verbose":{"type":"string","enum":["low","medium","high"],"default":"medium","description":"Level of detail in the model's response"}}},"tools":{"type":"array","description":"Tools available to the AI agent during processing.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["web_search","mcp","image_generation","tool"],"description":"Type of tool."},"webSearch":{"type":"object","description":"Web search configuration (empty object to enable)"},"imageGeneration":{"type":"object","description":"Image generation configuration","properties":{"background":{"type":"string","description":"Controls whether generated images have a transparent or opaque background; use transparent only with output formats that support it (png, webp).","enum":["transparent","opaque"]},"quality":{"type":"string","description":"Rendering quality of generated images, trading detail for generation speed and file size.","enum":["low","medium","high"]},"size":{"type":"string","description":"Pixel dimensions of generated images; choose square, portrait, or landscape to match the intended use.","enum":["1024x1024","1024x1536","1536x1024"]},"outputFormat":{"type":"string","description":"File format of generated images; use png or webp when transparency is needed.","enum":["png","webp","jpeg"]}}},"mcp":{"type":"object","description":"MCP server tool configuration","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server"},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if\nomitted). Each entry is either a plain tool name (legacy\nform) or an object carrying display metadata.","items":{"type":["string","object"],"properties":{"name":{"type":"string","maxLength":256,"description":"Tool name as exposed by the MCP server."},"title":{"type":"string","maxLength":300,"description":"Display title shown for the tool."},"description":{"type":"string","maxLength":1000,"description":"Display description shown for the tool."}},"required":["name"]}},"allowedPrompts":{"type":"array","description":"Specific prompts to allow from the MCP server (used for MCP prompt entries; all if omitted).","items":{"type":"string"}}}},"tool":{"type":"object","description":"Reference to a Celigo Tool resource.\n","properties":{"_toolId":{"type":"string","format":"objectId","description":"Reference to the Tool resource"},"overrides":{"type":"object","description":"Per-agent overrides for the tool's internal resources","properties":{"connections":{"type":"array","description":"Remaps the tool's abstract connections for this agent. Each entry pairs\nthe tool's abstract connection placeholder (`_abstractId`) with the\nconcrete connection (`_id`) to use for this agent; entries without\n`_id` keep the tool's own default connection.\n","items":{"type":["object","null"],"required":["_abstractId"],"properties":{"_abstractId":{"type":"string","format":"objectId","description":"The tool's abstract connection placeholder being overridden."},"_id":{"type":"string","format":"objectId","description":"Concrete connection to use in place of the abstract placeholder."}}}}}}}}}}},"prompts":{"type":"array","description":"MCP prompt entries available to the agent. Each item references one MCP connection\nand the prompt names allowed from it. Configured alongside `tools` in the form but\nstored separately; an entry's `allowedPrompts` is what distinguishes a prompt entry\nfrom an MCP tool entry (which carries `allowedTools`).\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","description":"MCP server prompt configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedPrompts":{"type":"array","description":"Prompt names to allow from the MCP server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"}}},"litellm":{"type":"object","description":"LiteLLM proxy configuration. Used when `provider` is \"gemini\" or \"anthropic\".\n\nLiteLLM provides a unified interface to multiple AI providers. Gemini-specific\nsettings are in `_overrides.gemini`; Claude-specific settings are in\n`_overrides.anthropic`.\n\n`model` is required when litellm is the active provider path.\n","properties":{"model":{"type":"string","description":"LiteLLM model identifier. For Gemini, models are stored without the `gemini/`\nprefix; for Anthropic, use the Claude model id (e.g. `claude-sonnet-4-6`)."},"temperature":{"type":"number","minimum":0,"maximum":2,"description":"Sampling temperature"},"maxCompletionTokens":{"type":"number","minimum":100,"maximum":128000,"default":5000,"description":"Maximum number of tokens in the response"},"topP":{"type":"number","minimum":0.1,"maximum":1,"description":"Nucleus sampling parameter"},"seed":{"type":"number","description":"Random seed for reproducible outputs"},"responseFormat":{"type":"object","description":"Output format configuration","properties":{"type":{"type":"string","description":"Output format type.","enum":["text","json_schema","blob"],"default":"text"},"schemaMode":{"type":"string","enum":["manual","json"],"description":"How the structured-output schema was authored in the UI. Editor\nstate only — it does not change how `jsonSchema` is sent to the\nprovider."},"name":{"type":"string","description":"Name for the output format (used with json_schema)."},"strict":{"type":"boolean","description":"When true, enforces strict schema validation on output.","default":false},"jsonSchema":{"type":"object","description":"JSON Schema for structured output. Required when `responseFormat.type` is \"json_schema\".","properties":{"type":{"type":"string","description":"Root JSON Schema type of the structured output; use \"object\" for record-shaped results.","enum":["object","array","string","number","integer","boolean"]},"properties":{"type":"object","additionalProperties":true,"description":"JSON Schema definitions for each field the structured output may contain."},"required":{"type":"array","description":"Property names the model must include in the structured output.","items":{"type":"string"}},"additionalProperties":{"type":"boolean","description":"When true, the structured output may include properties beyond those defined in `properties`."}}}},"if":{"properties":{"type":{"const":"json_schema"}},"required":["type"]},"then":{"required":["name","jsonSchema"]}},"_overrides":{"type":"object","description":"Provider-specific overrides","properties":{"gemini":{"type":"object","description":"Gemini-specific configuration overrides.\n","required":["systemInstruction"],"properties":{"systemInstruction":{"type":"string","maxLength":1000000,"description":"System instruction for Gemini models. Equivalent to OpenAI's `instructions`.\nMaximum 1,000,000 characters.\n"},"tools":{"type":"array","description":"Gemini-specific tools","items":{"type":"object","properties":{"type":{"type":"string","enum":["googleSearch","urlContext","fileSearch","mcp","tool"],"description":"Type of Gemini tool."},"googleSearch":{"type":"object","description":"Google Search configuration (empty object to enable)"},"urlContext":{"type":"object","description":"URL context configuration (empty object to enable)"},"fileSearch":{"type":"object","description":"File search configuration, used when type is \"fileSearch\".","properties":{"fileSearchStoreNames":{"type":"array","description":"Names of the file search stores the model can query.","items":{"type":"string"}}}},"mcp":{"type":"object","description":"MCP server tool configuration, used when type is \"mcp\".","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if\nomitted). Each entry is either a plain tool name\n(legacy form) or an object carrying display metadata\n— same contract as the OpenAI `allowedTools`.","items":{"type":["string","object"],"properties":{"name":{"type":"string","maxLength":256,"description":"Tool name as exposed by the MCP server."},"title":{"type":"string","maxLength":300,"description":"Display title shown for the tool."},"description":{"type":"string","maxLength":1000,"description":"Display description shown for the tool."}},"required":["name"]}},"allowedPrompts":{"type":"array","description":"Specific prompts to allow from the MCP server (used for MCP prompt entries; all if omitted).","items":{"type":"string"}}}},"tool":{"type":"object","description":"Reference to a Celigo Tool resource, used when type is \"tool\".","properties":{"_toolId":{"type":"string","format":"objectId","description":"Reference to the Tool resource."},"overrides":{"type":"object","description":"Per-agent overrides for the tool's internal resources.","properties":{"connections":{"type":"array","description":"Remaps the tool's abstract connections for this agent. Each entry\npairs the tool's abstract connection placeholder (`_abstractId`)\nwith the concrete connection (`_id`) to use for this agent;\nentries without `_id` keep the tool's own default connection.\n","items":{"type":["object","null"],"required":["_abstractId"],"properties":{"_abstractId":{"type":"string","format":"objectId","description":"The tool's abstract connection placeholder being overridden."},"_id":{"type":"string","format":"objectId","description":"Concrete connection to use in place of the abstract placeholder."}}}}}}}}}}},"prompts":{"type":"array","description":"MCP prompt entries available to the Gemini agent. Each item references one\nMCP connection and the prompt names allowed from it. The presence of\n`allowedPrompts` distinguishes a prompt entry from an MCP tool entry.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","description":"MCP server prompt configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedPrompts":{"type":"array","description":"Prompt names to allow from the MCP server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"},"responseModalities":{"type":"array","description":"Response output modalities","items":{"type":"string","enum":["text","image"]},"default":["text"]},"topK":{"type":"number","description":"Top-K sampling parameter for Gemini"},"thinkingConfig":{"type":"object","description":"Controls Gemini's extended thinking capabilities","properties":{"includeThoughts":{"type":"boolean","description":"When true, includes the model's thinking steps in the response."},"thinkingBudget":{"type":"number","minimum":100,"maximum":4000,"description":"Maximum tokens allocated for thinking"},"thinkingLevel":{"type":"string","description":"Controls how much thinking effort the model applies; use higher levels for complex, multi-step tasks at the cost of latency and tokens.","enum":["minimal","low","medium","high"]}}},"imageConfig":{"type":"object","description":"Gemini image generation configuration","properties":{"aspectRatio":{"type":"string","description":"Aspect ratio of generated images; choose a ratio matching the intended display format.","enum":["1:1","2:3","3:2","3:4","4:3","4:5","5:4","9:16","16:9","21:9"]},"imageSize":{"type":"string","description":"Output resolution of generated images; higher resolutions increase detail and file size.","enum":["1K","2K","4K"]}}},"mediaResolution":{"type":"string","enum":["low","medium","high"],"description":"Resolution for media inputs (images, video)"}}},"anthropic":{"type":"object","description":"Claude-specific configuration overrides. Used when `provider` is \"anthropic\".\n","required":["systemInstruction"],"properties":{"systemInstruction":{"type":"string","maxLength":1000000,"description":"System instruction for Claude models. Equivalent to OpenAI's `instructions`.\nMaximum 1,000,000 characters.\n"},"topK":{"type":"number","minimum":0,"description":"Top-K sampling parameter. Deprecated on Claude models released after Claude Opus\n4.6, which reject any value at runtime; set it only on older models."},"thinkingConfig":{"type":"object","description":"Controls Claude's extended thinking capabilities.","properties":{"type":{"type":"string","enum":["enabled","disabled","adaptive"],"default":"disabled","description":"Extended thinking mode."},"budgetTokens":{"type":"number","minimum":1024,"description":"Maximum tokens allocated for thinking. Required when `type` is \"enabled\" —\nomitting it fails the save with 422 `invalid_thinking_config`."},"display":{"type":"string","enum":["summarized","omitted"],"default":"summarized","description":"How thinking output is surfaced in the response."},"effort":{"type":"string","enum":["low","medium","high","xhigh","max"],"description":"How much thinking effort the model applies; use with `type` \"adaptive\". Higher\nvalues (`xhigh`, `max`) may be gated to specific Claude models by the provider."}},"if":{"properties":{"type":{"const":"enabled"}},"required":["type"]},"then":{"required":["budgetTokens"]}},"serviceTier":{"type":"string","enum":["auto","standard_only"],"default":"auto","description":"Anthropic service tier for the request."},"tools":{"type":"array","description":"Claude-specific tools.","items":{"type":"object","properties":{"type":{"type":"string","enum":["tool","mcp","webSearch"],"description":"Type of Claude tool."},"tool":{"type":"object","description":"Reference to a Celigo Tool resource, used when type is \"tool\".","properties":{"_toolId":{"type":"string","format":"objectId","description":"Reference to the Tool resource."},"overrides":{"type":"object","description":"Per-agent overrides for the tool's internal resources.","properties":{"connections":{"type":"array","description":"Remaps the tool's abstract connections for this agent. Each entry\npairs the tool's abstract connection placeholder (`_abstractId`)\nwith the concrete connection (`_id`) to use for this agent;\nentries without `_id` keep the tool's own default connection.\n","items":{"type":["object","null"],"required":["_abstractId"],"properties":{"_abstractId":{"type":"string","format":"objectId","description":"The tool's abstract connection placeholder being overridden."},"_id":{"type":"string","format":"objectId","description":"Concrete connection to use in place of the abstract placeholder."}}}}}}}},"mcp":{"type":"object","description":"MCP server tool configuration, used when type is \"mcp\".","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if omitted).","items":{"type":"string"}}}},"webSearch":{"type":"object","description":"Web search configuration, used when type is \"webSearch\".","properties":{"version":{"type":"string","pattern":"^\\d{8}$","description":"Anthropic web search tool version (YYYYMMDD). Selects the tool version sent\non the wire; unsupported versions surface as an Anthropic 400."},"allowedDomains":{"type":"array","description":"Domains the search may return results from. Mutually exclusive with `blockedDomains`.","items":{"type":"string"}},"blockedDomains":{"type":"array","description":"Domains to exclude from search results. Mutually exclusive with `allowedDomains`.","items":{"type":"string"}},"userLocation":{"type":"object","description":"Approximate user location used to localize search results. When present, at\nleast one of `city`, `country`, `region`, or `timezone` must be set.","properties":{"type":{"type":"string","enum":["approximate"],"description":"Location type. Always \"approximate\"."},"city":{"type":"string","maxLength":256,"description":"City name for localizing search results."},"country":{"type":"string","maxLength":8,"description":"ISO 3166-1 alpha-2 country code for localizing search results."},"region":{"type":"string","maxLength":256,"description":"Region or state for localizing search results."},"timezone":{"type":"string","maxLength":64,"description":"IANA timezone for localizing search results."}}}}}}}},"prompts":{"type":"array","description":"MCP prompt entries available to the Claude agent. Each item references one MCP\nconnection and the prompt names allowed from it. The presence of `allowedPrompts`\ndistinguishes a prompt entry from an MCP tool entry.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","description":"MCP server prompt configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedPrompts":{"type":"array","description":"Prompt names to allow from the MCP server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"}}}}}}}},"if":{"properties":{"provider":{"const":"openai"}},"required":["provider"]},"then":{"required":["openai"],"properties":{"openai":{"required":["model","instructions"]}}},"else":{"if":{"properties":{"provider":{"enum":["gemini","anthropic"]}},"required":["provider"]},"then":{"required":["litellm"],"properties":{"litellm":{"required":["model"]}}},"else":{"if":{"properties":{"provider":{"enum":["mistral","xai","huggingface","deepseek","cohere","groq"]}},"required":["provider"]},"then":{"required":["model"]}}}},"McpResources":{"type":"array","description":"Governed MCP resources — read-only reference content (policies, schemas, documentation)\npulled from connected MCP servers and made available to the agent as a consistent source\nof truth. Each entry references one MCP connection and the specific resources allowed from it.","items":{"type":"object","required":["type","mcp"],"properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of resource entry. Always \"mcp\"."},"mcp":{"type":"object","required":["_mcpConnectionId","allowedResources"],"description":"MCP server resource configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server that exposes the resources."},"allowedResources":{"type":"array","minItems":1,"description":"Resources to expose to the agent from the MCP server. Must contain at least one\nentry; each entry identifies one resource by name and URI.","items":{"type":"object","required":["name","uri"],"properties":{"name":{"type":"string","description":"Display name of the MCP resource."},"uri":{"type":"string","description":"URI that identifies the resource on the MCP server."}}}}}}}}},"GuardrailConfig":{"type":"object","description":"Configuration for GuardrailImport adaptor type.\n\nGuardrails evaluate data flowing through integrations for safety and\ncompliance. The `type` field selects which check to apply, and the\ncorresponding sub-object (`aiAgent`, `pii`, or `moderation`) provides\nthe configuration.\n\nA `_connectionId` on the parent import is only needed for BYOK\n`ai_agent` guardrails. In responses the server echoes the active type's\nsub-object and applies the `confidenceThreshold` default; it also\nreturns inactive sibling sub-objects (e.g. `moderation: {categories: []}`\non a `pii` guardrail, or a populated `pii` left over from a type switch),\nbut only the active type's sub-object is meaningful. Legacy documents\nmay carry a server-written default `aiAgent` stub on `pii`/`moderation`\nguardrails; current servers strip the inactive `aiAgent` on write.","properties":{"type":{"type":"string","enum":["ai_agent","pii","moderation"],"description":"The type of guardrail to apply. Each type requires its corresponding\nsub-configuration object (`aiAgent`, `pii`, or `moderation`)."},"confidenceThreshold":{"type":"number","minimum":0,"maximum":1,"default":0.7,"description":"Confidence threshold (0 to 1). Detections below this threshold are\nignored. Lower values catch more issues but increase false positives."},"aiAgent":{"type":"object","description":"AI agent check configuration; set when `type` is `ai_agent`. On\n`pii`/`moderation` guardrails a legacy server-written stub may\nappear here — it is inert, and current servers strip it on write."},"pii":{"type":"object","required":["entities"],"description":"PII detection configuration. Required when `type` is `pii`.","properties":{"entities":{"type":"array","description":"PII entity types to detect. When `type` is `pii`, at least one\nentry is required; the inactive sibling on other guardrail types\nmay be served with an empty list.","items":{"type":"string","enum":["credit_card_number","card_security_code_cvv_cvc","cryptocurrency_wallet_address","date_and_time","email_address","iban_code","bic_swift_bank_identifier_code","ip_address","location","medical_license_number","national_registration_number","persons_name","phone_number","url","us_bank_account_number","us_drivers_license","us_itin","us_passport_number","us_social_security_number","uk_nhs_number","uk_national_insurance_number","spanish_nif","spanish_nie","italian_fiscal_code","italian_drivers_license","italian_vat_code","italian_passport","italian_identity_card","polish_pesel","finnish_personal_identity_code","singapore_nric_fin","singapore_uen","australian_abn","australian_acn","australian_tfn","australian_medicare","indian_pan","indian_aadhaar","indian_vehicle_registration","indian_voter_id","indian_passport","korean_resident_registration_number"]}},"mask":{"type":"boolean","default":false,"description":"When true, detected PII is replaced with masked values.\nWhen false, PII is flagged without modification."}}},"moderation":{"type":"object","required":["categories"],"description":"Content moderation configuration. Required when `type` is `moderation`.","properties":{"categories":{"type":"array","description":"Content moderation categories to check. When `type` is\n`moderation`, at least one entry is required; the inactive\nsibling on other guardrail types may be served with an empty\nlist.","items":{"type":"string","enum":["sexual","sexual_minors","hate","hate_threatening","harassment","harassment_threatening","self_harm","self_harm_intent","self_harm_instructions","violence","violence_graphic","illicit","illicit_violent"]}}}}},"required":["type","confidenceThreshold"],"if":{"required":["type"],"properties":{"type":{"const":"pii"}}},"then":{"required":["pii"],"properties":{"pii":{"required":["entities"],"properties":{"entities":{"minItems":1}}}}},"else":{"if":{"required":["type"],"properties":{"type":{"const":"moderation"}}},"then":{"required":["moderation"],"properties":{"moderation":{"required":["categories"],"properties":{"categories":{"minItems":1}}}}},"else":{"required":["aiAgent"],"properties":{"aiAgent":{"$ref":"#/components/schemas/AiAgentConfig"}}}}},"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"},"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\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**: Using wildcards like `*` and `?`\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- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"isempty\"`: Field is empty or null (is empty)\n- `\"isnotempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches specified pattern (matches)\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- Returns the raw field value which may need type conversion\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  - `export.settingName`: Access export-level settings\n  - `import.settingName`: Access import-level settings\n  - `integration.settingName`: Access integration-level settings\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\nType conversion operators are often necessary when comparing extracted field values against literals or when the field type doesn't match the comparison operator's expected type. 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    \"extract\",\n    \"status\"\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"extract\",\n    \"filename\"\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\n```json\n[\n  \"greaterthan\",\n  [\n    \"extract\",\n    \"orderDate\"\n  ],\n  \"2023-01-01T00:00:00Z\"\n]\n```\n\nExample 5: Category contains any of [\"Urgent\", \"High Priority\"]\n```json\n[\n  \"anyof\",\n  [\n    \"extract\",\n    \"category\"\n  ],\n  [\"Urgent\", \"High Priority\"]\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 that exists in the system.\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"}}}}},"Hook":{"type":"object","description":"A single lifecycle hook — a JavaScript function invoked from a script (or hosted on a\nstack) at a fixed point in an import or AI-agent run. Shared by every hook slot; the\nslot's own description says when in the lifecycle it fires.","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","null"],"description":"Static parameters passed to the hook function at runtime, letting one script be\nreused with different settings. Null when the hook carries no static parameters."}}},"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.\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"}}}}},"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, arrays of objects, arrays of arrays of objects) are ALL\n   handled through this recursive pattern\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","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- For arrays within arrays: Leave EMPTY for the inner array mappings, as they don't have field names\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, empty 'generate' fields almost always indicate inner array structures\nwithin a parent array.\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, attempts conversion from strings\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. REQUIRES child mappings in the 'mappings' array\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- `arrayarray`: Array of arrays (for matrix/table structures)\n\nArray dataTypes can be populated two ways: pass a source array through\nunchanged via `extract` alone (when the source is already an array of\nthe right shape), or construct/iterate via `buildArrayHelper`.\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` is one option (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","arrayarray"]},"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\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 `object`, `objectarray`, and primitive-array dataTypes as\n  well as scalars — the rendered template output must parse into the\n  declared shape\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- When both extract and mappings are defined for objects, extract is applied first\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 empty (the UI's \"Do nothing\" action).\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.\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"},"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, date)\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 REQUIRED mechanism for ALL array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.)\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**For Arrays of Arrays (arrayarray)**\n- Similar to objectarray, but inner arrays have empty 'generate' fields\n- Used for matrix/table structures\n```json\n{\n  \"generate\": \"matrix\",\n  \"dataType\": \"arrayarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.rows[*]\",  // For each row in the rows array\n      \"mappings\": [\n        {\n          \"dataType\": \"numberarray\",  // Note: No generate field for inner arrays\n          \"buildArrayHelper\": [\n            {\"extract\": \"$.rows.columns[*]\"}  // Notice: \"rows\" is singular in the composite object\n          ]\n        }\n      ]\n    }\n  ]\n}\n```\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"},"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":"Specifies the condition that must be met for an array element to be included.\n\n'extract_not_empty' only includes elements where the extract field returns a value.\n","enum":["extract_not_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"}}}}},"status":{"type":"string","description":"**Purpose**\nRequired on every mapping entry. Controls whether the mapping is applied.\n\n**Guidance**\nEmit `\"Active\"` for mappings that should run; `\"Draft\"` saves an\nin-progress mapping without the Active-only field validations. The\nAPI rejects a mapping missing this field\n(validation error: \"Mapping object must have status field present.\").\n","enum":["Active","Draft"]},"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**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":"Specifies the condition that triggers application of this mapping:\n- record_created: Apply only when creating a new record\n- record_updated: Apply only when updating an existing record\n- extract_not_empty: Apply only when the extract field returns a value\n","enum":["record_created","record_updated","extract_not_empty"]}}}},"required":["dataType"]}},"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- For arrays within arrays: Leave EMPTY for the inner array mappings, as they don't have field names\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, empty 'generate' fields almost always indicate inner array structures\nwithin a parent array.\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, attempts conversion from strings\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. REQUIRES child mappings in the 'mappings' array\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- `arrayarray`: Array of arrays (for matrix/table structures)\n\nArray dataTypes can be populated two ways: pass a source array through\nunchanged via `extract` alone (when the source is already an array of\nthe right shape), or construct/iterate via `buildArrayHelper`.\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` is one option (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","arrayarray"]},"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\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 `object`, `objectarray`, and primitive-array dataTypes as\n  well as scalars — the rendered template output must parse into the\n  declared shape\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- When both extract and mappings are defined for objects, extract is applied first\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 empty (the UI's \"Do nothing\" action).\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.\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"},"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, date)\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 REQUIRED mechanism for ALL array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.)\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**For Arrays of Arrays (arrayarray)**\n- Similar to objectarray, but inner arrays have empty 'generate' fields\n- Used for matrix/table structures\n```json\n{\n  \"generate\": \"matrix\",\n  \"dataType\": \"arrayarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.rows[*]\",  // For each row in the rows array\n      \"mappings\": [\n        {\n          \"dataType\": \"numberarray\",  // Note: No generate field for inner arrays\n          \"buildArrayHelper\": [\n            {\"extract\": \"$.rows.columns[*]\"}  // Notice: \"rows\" is singular in the composite object\n          ]\n        }\n      ]\n    }\n  ]\n}\n```\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"},"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":"Specifies the condition that must be met for an array element to be included.\n\n'extract_not_empty' only includes elements where the extract field returns a value.\n","enum":["extract_not_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"}}}}},"status":{"type":"string","description":"**Purpose**\nRequired on every mapping entry. Controls whether the mapping is applied.\n\n**Guidance**\nEmit `\"Active\"` for mappings that should run; `\"Draft\"` saves an\nin-progress mapping without the Active-only field validations. The\nAPI rejects a mapping missing this field\n(validation error: \"Mapping object must have status field present.\").\n","enum":["Active","Draft"]},"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**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":"Specifies the condition that triggers application of this mapping:\n- record_created: Apply only when creating a new record\n- record_updated: Apply only when updating an existing record\n- extract_not_empty: Apply only when the extract field returns a value\n","enum":["record_created","record_updated","extract_not_empty"]}}}},"required":["dataType"]},"MappingField":{"type":"object","description":"One Mapper 1.0 field-mapping entry.","properties":{"generate":{"type":"string","description":"Target field path to write on the destination record."},"extract":{"type":"string","description":"Source expression. Accepts:\n- JSONPath starting with `$.` (e.g. `$.customer.firstName`).\n- Handlebars template (contains `{{`) for concatenation /\n  formatting / conditional logic (e.g.\n  `{{record.firstName}} {{record.lastName}}`).\n- A literal string (any value not starting with `$.` and not\n  containing `{{`) — treated as a hardcoded value.\n\nOmit when using `hardCodedValue`.\n"},"hardCodedValue":{"type":["string","null"],"description":"Static value written to `generate` instead of extracting from the source record."},"dataType":{"type":"string","enum":["string","number","boolean","numberarray","stringarray","json"],"description":"Data type coercion applied to the mapped value."},"discardIfEmpty":{"type":"boolean","description":"When true, the field is omitted from the output when the extracted value is empty."},"immutable":{"type":"boolean","description":"When true, the mapped value cannot be overwritten by later mapping steps."},"lookupName":{"type":"string","description":"Name of an entry in the import's `lookups` array used to translate the value."},"default":{"type":["string","null"],"description":"Fallback value used when the extract yields no value."},"extractDateFormat":{"type":"string","description":"Date format of the source value, used to parse it before conversion."},"extractDateTimezone":{"type":"string","description":"Timezone applied when parsing the source date value."},"generateDateFormat":{"type":"string","description":"Date format applied to the value written to the destination."},"generateDateTimezone":{"type":"string","description":"Timezone applied when formatting the destination date value."},"conditional":{"type":"object","description":"Only apply this mapping entry when the given condition is satisfied.\n","properties":{"when":{"type":"string","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","expression"],"description":"Condition that gates whether this mapping entry is applied.\n`lookup_not_empty` / `lookup_empty` evaluate the lookup named by the\nsibling `lookupName`; `expression` evaluates the sibling `expression`.\n"},"lookupName":{"type":"string","description":"Lookup to evaluate for the `lookup_not_empty` / `lookup_empty`\nconditions.\n"},"expression":{"type":"string","description":"Expression evaluated when `when` is `expression`.\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.\nThis value typically matches the key in the fieldMap object.\n"},"name":{"type":"string","description":"Name of the field, used as the property name when generating the settings object\nfrom the submitted form data.\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.\n","default":false},"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 fields, specifies the placeholder and field name for the key input.\n"},"valueName":{"type":"string","description":"For keyvalue fields, specifies the placeholder and field name for the value input.\n"},"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.\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"]},"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.\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","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. This script must be accessible within the user's account\nand have appropriate permissions.\n"}}}}},"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"}}},"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},"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":"Reference to the integration app that defines this resource.\n\nThis field is only populated for resources that are part of an integration app.\nIt contains the unique identifier (_id) of the integration app (connector)\nthat defines the structure, behavior, and templates for this resource.\n\nThe integration app is the published template that can be installed\nmultiple times across different accounts, with each installation creating\na separate integration instance (referenced by _integrationId).\n\nThis reference enables:\n- Identifying the source integration app for this resource\n- Determining which template version is being used\n- Linking to documentation, support, and marketplace information\n"}}},"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/imports/{_id}":{"get":{"summary":"Get an import","description":"Returns the complete configuration of a specific import.\n","operationId":"getImportById","tags":["Imports"],"parameters":[{"name":"_id","in":"path","description":"The unique identifier of the import","required":true,"schema":{"type":"string","format":"objectId"}}],"responses":{"200":{"description":"Import retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Import"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
````

## Update an import

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

````json
{"openapi":"3.2.0","info":{"title":"Imports","version":"1.0.0"},"tags":[{"name":"Imports","description":"Imports deliver data to destination systems. An import receives pages of records from\nupstream flow steps, applies field mappings and transformations, and writes the results\nto the target application, database, or file destination — handling lookups, duplicate\navoidance, and per-record error reporting along the way. Each import uses one\n`adaptorType` that determines its connection compatibility and configuration object.\n\n## Import schema\n\n{% openapi-schemas spec=\"import\" schemas=\"Import\" 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 import. Set the adaptor-specific\nconfiguration object matching `adaptorType` (e.g. `netsuite_da` for `NetSuiteDistributedImport`).\n`_connectionId` is required except for the connection-less flavors (`ToolImport`,\n`AiAgentImport`, `GuardrailImport`): a `ToolImport` binds connections through the\nreferenced tool's `overrides`, and AI agent / guardrail imports only use a connection\nfor BYOK.","required":["name"],"allOf":[{"$ref":"#/components/schemas/ImportBase"}],"if":{"anyOf":[{"required":["adaptorType"],"properties":{"adaptorType":{"enum":["ToolImport","AiAgentImport","GuardrailImport"]}}},{"required":["tool"]},{"required":["aiAgent"]},{"required":["guardrail"]}]},"else":{"required":["_connectionId"],"if":{"properties":{"adaptorType":{"const":"NetSuiteHTTPImport"}},"required":["adaptorType"]},"then":{"required":["http","nsDomainType"]}}},"ImportBase":{"type":"object","description":"Writable import fields shared by the request and response schemas.","properties":{"_connectionId":{"type":"string","format":"objectId","description":"Connection this import uses to reach the destination system. The connection's type must\nbe compatible with the import's `adaptorType` (e.g. an `HTTPImport` needs an `http`\nconnection; `NetSuiteDistributedImport` and `NetSuiteHTTPImport` both need a `netsuite`\nconnection). Server-required — POST without it fails with 422 \"Expected field:\n_connectionId to be present\" — except for the connection-less flavors (`ToolImport`,\n`AiAgentImport`, `GuardrailImport`), which the server creates without one."},"_integrationId":{"type":["string","null"],"format":"objectId","description":"Integration this import belongs to."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this import was created from, set when the import is part of an installed integration app."},"adaptorType":{"type":"string","description":"Selects the adaptor technology that executes this import, which determines the compatible\nconnection types and which adaptor-specific configuration object must also be supplied\n(e.g. set `salesforce` when using `SalesforceImport`).","enum":["HTTPImport","FTPImport","AS2Import","S3Import","NetSuiteImport","NetSuiteDistributedImport","NetSuiteHTTPImport","SalesforceImport","JDBCImport","RDBMSImport","MongodbImport","DynamodbImport","WrapperImport","AiAgentImport","GuardrailImport","FileSystemImport","ToolImport","RESTImport"]},"nsDomainType":{"type":"string","enum":["suitetalk","restlet"],"description":"Selects which NetSuite REST host a `NetSuiteHTTPImport` 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 each\n`http.relativeURI` entry carries the complete path starting at `/services/rest/...` or\n`/app/site/hosting/restlet.nl`. Ignored on other adaptor types."},"externalId":{"type":["string","null"],"description":"External identifier for correlating the import with a record in another system."},"as2":{"$ref":"#/components/schemas/As2"},"dynamodb":{"$ref":"#/components/schemas/Dynamodb"},"http":{"$ref":"#/components/schemas/Http"},"ftp":{"$ref":"#/components/schemas/Ftp"},"jdbc":{"$ref":"#/components/schemas/Jdbc"},"mongodb":{"$ref":"#/components/schemas/Mongodb"},"netsuite":{"$ref":"#/components/schemas/NetSuite"},"netsuite_da":{"$ref":"#/components/schemas/NetsuiteDistributed"},"rdbms":{"$ref":"#/components/schemas/Rdbms"},"s3":{"$ref":"#/components/schemas/S3"},"wrapper":{"$ref":"#/components/schemas/Wrapper"},"salesforce":{"$ref":"#/components/schemas/Salesforce"},"tool":{"$ref":"#/components/schemas/Tool"},"file":{"$ref":"#/components/schemas/File"},"filesystem":{"$ref":"#/components/schemas/FileSystem"},"aiAgent":{"$ref":"#/components/schemas/AiAgentConfig"},"guardrail":{"$ref":"#/components/schemas/GuardrailConfig"},"name":{"type":"string","minLength":1,"maxLength":100,"description":"Display name for the import, shown in the flow builder, job history, and error logs.\nDescriptive, unique names indicating the destination system and purpose make large\naccounts easier to manage."},"description":{"type":["string","null"],"description":"Free-text summary of what the import writes and why. Shown in the UI and available to\nAI agents for context; has no effect on execution.","maxLength":5120},"unencrypted":{"type":"object","description":"Custom configuration values stored without encryption and returned in API responses."},"sampleData":{"type":["object","array","string"],"description":"Sample input record used to preview and build the import's mappings."},"distributed":{"type":"boolean","description":"When true, the import uses a distributed adaptor (such as `NetSuiteDistributedImport`)\nthat executes inside the target application rather than on Celigo's servers."},"maxAttempts":{"type":"number","description":"Maximum number of attempts made to deliver a record before it is marked as failed."},"ignoreExisting":{"type":"boolean","description":"When true, records that already exist in the destination system are silently skipped\ninstead of being created or updated — used for create-only operations that must avoid\nduplicates. Existing records are identified by the import's lookup configuration or by\na populated `ignoreExtract` field on the incoming record."},"ignoreMissing":{"type":"boolean","description":"When true, records that do not already exist in the destination system are silently\nskipped instead of producing errors — used for update-only operations."},"idLockTemplate":{"type":["string","null"],"description":"Handlebars template that generates a lock key for each record so records resolving to\nthe same key are not submitted concurrently, preventing duplicate or conflicting writes\nto the same target record."},"dataURITemplate":{"type":["string","null"],"description":"Handlebars template that builds a link back to each record in the destination\napplication's UI. The resolved URL is stored with error records in job history so users\ncan jump straight to the record."},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"blobKeyPath":{"type":"string","description":"Path in the input record that holds the blob key identifying the file content to import.\nAt send time the platform follows this path, retrieves the referenced file from integrator.io\nstorage, and streams its bytes into the outgoing request."},"blob":{"type":"boolean","description":"When true, this import transfers raw file content (blobs) to the destination rather than structured records."},"assistant":{"type":"string","description":"Identifier for the connector assistant used to configure this import."},"deleteAfterImport":{"type":"boolean","description":"When true, the source file is deleted after it is successfully imported."},"assistantMetadata":{"type":"object","description":"Metadata associated with the connector assistant configuration."},"useTechAdaptorForm":{"type":"boolean","description":"When true, the UI presents the full technical adaptor form for this import instead of\nthe simplified assistant form."},"distributedAdaptorData":{"type":"object","description":"Internal state stored by distributed adaptors (such as the NetSuite SuiteApp) for this import."},"filter":{"description":"Filter applied to incoming records before they are sent to the destination system.\nRecords that match continue through the import; records that don't are silently\ndropped. Filter expressions reference the incoming record's fields.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"traceKeyTemplate":{"type":"string","description":"Handlebars template that overrides how each record's unique trace key is generated,\nused to track records through the flow and match errors to records. Trace keys are\ncapped at 256 characters; a longer key is stored truncated from the middle, keeping\nthe beginning and end of the value."},"mockResponse":{"type":"array","description":"Predefined response records used in place of calling the destination system when\ntesting the import, so flows can run without writing real data. Records must be in\nintegrator.io canonical format — the server rejects any other shape with a 422.","items":{"type":"object","properties":{"statusCode":{"type":"integer","description":"HTTP status code the mock returns (e.g. 200, 403)."},"id":{"type":["string","integer"],"description":"Identifier echoed for the mocked record."},"ignored":{"type":"boolean","description":"When true, the mocked record reports as ignored rather than imported."},"dataURI":{"type":"string","description":"Data URI echoed for the mocked record."},"errors":{"type":"array","description":"Mock error entries returned with the record."},"_json":{"type":["object","array"],"description":"Mock response body."},"_headers":{"type":"object","description":"Mock response headers."}}}},"_ediProfileId":{"type":"string","format":"objectId","description":"EDI profile this import uses to generate outbound X12 or EDIFACT documents — it supplies\nthe envelope qualifiers, delimiters, version, and validation rules. Set it when the\nimport produces EDI output; omit it otherwise. Accepted on the file-writing imports —\nFTP, AS2, S3 and HTTP file mode (`http.type: \"file\"`)."},"parsers":{"type":"array","description":"Legacy parser configuration slot. The server initializes this field to an empty array\nand current API writes never populate it; treat it as server bookkeeping rather than a\nsetting to configure."},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the import lifecycle for\ntransformations, validation, and custom result handling beyond what configuration alone\ncan express.","properties":{"preMap":{"type":"object","description":"Hook that runs on each page of records before the import's mappings are applied.\nCommonly used to reshape or filter records ahead of mapping.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postMap":{"type":"object","description":"Hook that runs after the import's mappings are applied but before records are sent\nto the destination system. Commonly used for adjustments that need the mapped\n(destination-shaped) record.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postSubmit":{"type":"object","description":"Hook that runs after the destination system responds, with access to both the\nsubmitted records and the destination's responses. Commonly used to inspect results\nor adjust the response data passed downstream.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postAggregate":{"type":"object","description":"Hook that runs after an aggregated file has been submitted to the destination, for\nfile-based imports that combine records into a single file.","allOf":[{"$ref":"#/components/schemas/Hook"}]}}},"sampleResponseData":{"type":["object","array","string"],"description":"Sample response payload used to preview response mappings and test downstream steps\nwithout calling the destination system."},"responseTransform":{"description":"Transformation that reshapes the destination system's response after records are\nimported, before the response is processed by response mappings and downstream\nsteps. Commonly used to extract relevant fields from verbose API responses.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"modelMetadata":{"type":"object","description":"Metadata about the destination data model captured for this import. Rarely set."},"mapping":{"type":"object","description":"Mapper 1.0 mapping configuration. `fields` maps individual fields; `lists` maps\nrecord lists, each with its own `fields` array. Superseded by Mapper 2.0\n(`mappings`) but still widely used.","properties":{"fields":{"type":"array","description":"Field-level mapping entries applied to each record.","items":{"$ref":"#/components/schemas/MappingField"}},"lists":{"type":"array","description":"List-level mappings, each generating a sublist on the target record.","items":{"type":"object","properties":{"generate":{"type":"string","description":"Target sublist or array path to generate."},"fields":{"type":"array","description":"Field mappings applied within each generated list item.","items":{"$ref":"#/components/schemas/MappingField"}}}}}}},"mappings":{"description":"Field mappings that transform incoming records into the destination system's field\nstructure — renaming fields, converting types, building nested objects, and applying\nformulas or lookups to derive values.","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"lookups":{"description":"Top-level mirror of the adaptor-specific ``lookups`` array. Celigo persists lookups\nin TWO locations on every import resource: ``<adaptor_key>.lookups`` (the typed,\nadaptor-specific definition) and this top-level ``lookups`` array (a permissive copy).","allOf":[{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the shape of the input passed to the import's processing pipeline."},"settingsForm":{"$ref":"#/components/schemas/Form"},"preSave":{"$ref":"#/components/schemas/PreSave"},"settings":{"$ref":"#/components/schemas/Settings"}}},"As2":{"type":"object","description":"Configures how outbound AS2 messages are built for this import. Required when the\n_connectionId field references an AS2 connection. AS2 (Applicability Statement 2) transmits\nEDI and other data securely over HTTP/S using S/MIME encryption and digital signatures; this\nobject names the payload file, the message identifier, retry behavior, and any extra headers.","required":["fileNameTemplate"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies the partner-specific EDI and AS2 configuration.\nWhen set, the import inherits the connector's pre-configured settings; omit to use only\nthe AS2 connection details."},"fileNameTemplate":{"type":"string","description":"Handlebars template that names the file carrying each AS2 message payload. Include a\nuniqueness token such as {{timestamp}} so concurrent or repeated sends do not collide;\ndefaults to file-{{timestamp}} in the form."},"messageIdTemplate":{"type":"string","description":"Handlebars template that generates the Message-ID header for each AS2 message. Placeholders\nare replaced at runtime; the resulting value must be globally unique and follow RFC 5322\nheader formatting. Leave unset to let the platform generate the Message-ID."},"maxRetries":{"type":"number","default":0,"description":"Number of times a failed transmission is retried after transient errors such as network\nfailures or timeouts; does not affect the initial send. The form offers 1–5; set to 0\n(the default) to report failure immediately without retrying."},"headers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header sent with the AS2 message (for example, `content-disposition`). Header names are case-insensitive."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions rendered against the record."}}},"description":"HTTP headers sent with the AS2 message transmission.\nSupports both standard AS2 headers and custom headers required by trading partner agreements."}}},"Dynamodb":{"type":"object","description":"Configuration for DynamoDB imports. Required when the _connectionId field references a\nDynamoDB connection; must not be included for other connection types. putItem writes a full\nitem document; updateItem modifies named attributes of an existing item identified by its\nkey.","required":["region","method","tableName"],"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"],"default":"us-east-1","description":"AWS region hosting the DynamoDB table. Determines the service endpoint used for all\nrequests and must match the region where the table is deployed."},"method":{"type":"string","enum":["putItem","updateItem"],"description":"Write operation performed against the DynamoDB table for each record."},"tableName":{"type":"string","description":"DynamoDB table that receives the imported records.\nMust match an existing table in the target AWS account and region; table names are case-sensitive."},"partitionKey":{"type":"string","description":"Value of the target item's partition key for the updateItem operation; together with\nsortKey (when the table defines one) it identifies the item each record writes to.\nSupports handlebars to reference the incoming record's fields."},"sortKey":{"type":"string","description":"Value of the target item's sort key; supports handlebars. Omit when the table defines only\na partition key."},"itemDocument":{"type":"string","description":"JSON document written to the table for the putItem operation, containing all attribute\nnames and values for the item, including its key attributes. Supports handlebars."},"updateExpression":{"type":"string","description":"DynamoDB update expression naming which attributes to set, add, or remove on the item for\nthe updateItem operation. Only the named attributes change; reference attribute names and\nvalues through the expressionAttributeNames and expressionAttributeValues placeholders."},"conditionExpression":{"type":"string","description":"Condition evaluated against the existing item before the write executes.\nWhen the condition evaluates to false, the operation is aborted and no changes are made."},"expressionAttributeNames":{"type":"string","description":"JSON placeholder-to-name map for attribute names referenced in expressions, for example {\"#N\": \"Name\"}.\nUse it when an attribute name is a reserved word or contains special characters; placeholder keys start with #."},"expressionAttributeValues":{"type":"string","description":"JSON placeholder-to-value map for attribute values referenced in expressions; placeholder\nkeys start with a colon (e.g. \":val\") and each value is a DynamoDB attribute-value object\nsuch as {\"S\": \"text\"}. Supports handlebars."},"ignoreExtract":{"type":"string","description":"Path to the field in the source record used to determine whether the record already exists.\nOnly used when ignoreExisting is set on the import."}},"if":{"properties":{"method":{"const":"updateItem"}},"required":["method"]},"then":{"required":["partitionKey","updateExpression"]}},"Http":{"type":"object","description":"Configuration for HTTP imports.\nRequired whenever the connection referenced by _connectionId has type http, and on\n`NetSuiteHTTPImport` (NetSuite's REST APIs on a `netsuite` connection, where `nsDomainType`\nsupplies the host).","properties":{"sendPostMappedData":{"type":"boolean","default":true,"description":"When true, the request body is the record as reshaped by the import's mapping step. When false, the original pre-mapped record is sent instead, bypassing the mappings for the payload."},"isRest":{"type":"boolean","description":"Internal flag indicating the import was built with the legacy REST form rather than the unified HTTP framework. Set by the form; not configured directly."},"strictHandlebarEvaluation":{"type":"boolean","description":"When true, handlebars expressions that reference a missing field raise an error instead of rendering an empty string, surfacing mapping mistakes early rather than sending blank values."},"formType":{"type":"string","enum":["assistant","http","rest","graph_ql","assistant_graphql"],"description":"Determines the UI form and configuration experience for this import."},"type":{"type":"string","enum":["file","records"],"description":"Controls whether the import sends structured record data or raw file content.\nMost HTTP imports use records; use file only when importing raw file content that will be processed downstream."},"requestMediaType":{"type":"string","enum":["xml","json","csv","urlencoded","form-data","octet-stream","plaintext"],"description":"Content type used to serialize the request body sent to the target API.\nMost REST APIs use json."},"_httpConnectorEndpointIds":{"type":"array","readOnly":true,"items":{"type":"string","format":"objectId"},"description":"HTTP connector endpoint IDs used by this import (multiple endpoints for different request\ntypes or operations). Set by the connector framework; client-supplied values are ignored."},"blobFormat":{"type":"string","enum":["utf8","ucs2","utf-16le","ascii","binary","base64","hex"],"description":"Character encoding format for blob/binary data imports.\nOnly relevant when type is \"file\" or when handling binary content.\n"},"batchSize":{"type":"integer","description":"Maximum number of records submitted per HTTP request, which affects throughput and API rate limiting.\nREST services typically send one record per request; raise it only for batch endpoints or RPC/XML services that accept multiple records, consulting the target API documentation for the optimal value.\n\nThe default varies by API."},"successMediaType":{"type":"string","enum":["xml","json","plaintext"],"description":"Media type expected in successful responses from the target API. Most APIs return json."},"requestType":{"type":"array","items":{"type":"string","enum":["CREATE","UPDATE"]},"description":"Operation type each request performs; for composite (upsert) imports, include both values.\nThe array is positionally aligned with the relativeURI and method arrays — each index maps to one operation — and the existingExtract field determines which operation runs at runtime."},"errorMediaType":{"type":"string","enum":["xml","json","plaintext"],"description":"Media type expected in error responses from the target API. Most APIs return json."},"followRedirects":{"type":"boolean","default":true,"description":"When explicitly false, 3xx responses are not followed — the redirect\nresponse itself (status code, `Location` header, body) is what the\nimport records as the result. Omitted or true follows allowed\nredirects (the default behavior)."},"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":"array","items":{"type":"string"},"description":"Relative URI path for each import request, as an array of strings (never an object); values can include Handlebars expressions for dynamic segments.\nTemplates render against the pre-mapped record — the original input before the Import's mapping step — because URI construction usually needs business identifiers that mappings may rename or remove.\nFor composite (upsert) imports, the array is positionally aligned with method and requestType, and existingExtract decides the index used at runtime: the UPDATE index when its field has a value, the CREATE index when it is empty or missing.\nReference the existing record ID in the UPDATE URI with `{{{data.0.fieldName}}}` (triple braces with the data.0 prefix).\nUse separate array elements per operation rather than `{{#if}}` conditionals inside a single URI."},"method":{"type":"array","items":{"type":"string","enum":["GET","PUT","POST","PATCH","DELETE"]},"description":"HTTP method used for each import request.\nFor composite (upsert) imports, the array is positionally aligned with relativeURI and requestType — e.g. `[\"PUT\", \"POST\"]` with `requestType: [\"UPDATE\", \"CREATE\"]` uses PUT for updates and POST for creates."},"_httpConnectorVersionId":{"type":"string","format":"objectId","readOnly":true,"description":"HTTP connector version used by this import. Set by the connector framework; client-supplied values are ignored (write-tested)."},"_httpConnectorResourceId":{"type":"string","format":"objectId","readOnly":true,"description":"HTTP connector resource used by this import. Set by the connector framework; client-supplied values are ignored (write-tested)."},"_httpConnectorEndpointId":{"type":"string","format":"objectId","readOnly":true,"description":"HTTP connector endpoint used by this import (single endpoint). Set by the connector framework; client-supplied values are ignored (write-tested)."},"body":{"type":"array","items":{"type":["string","null"]},"description":"Request body template for the import — an array of Handlebars template strings\npositionally aligned with the `method` array (e.g. `[\"{{{record}}}\"]`). A `null`\nentry means no template for the method at that position. Each entry is a string:\nwrite a JSON body as a template string (`[\"{\\\"id\\\": \\\"{{record.id}}\\\"}\"]`), not as\na JSON object, because an object entry does not render as JSON at runtime.\nLeave undefined for standard imports — the Import's mapping step transforms the source record and the mapped data is sent as the request body automatically.\nSet it only when the target API requires XML, SOAP, or a custom structure that mappings cannot produce, or when the user explicitly specifies a request body.\nWhen set, the mapping step still runs first and the template renders against the post-mapped record — use `{{record.<mappedField>}}` to reference mapping outputs.\nHand-templated bodies are harder to maintain and debug than mappings; when in doubt, leave this field undefined.\n\nCustom envelopes can combine static header or account fields with per-message\nfield references inside a loop.\n\nStatic wrappers around the mapped record are appropriate for message-bus class envelopes,\nHMAC signature wrappers, integration batch headers,\nnamed single-record array wrappers, and similar destination-required structures.\n\nBatch templates can wrap an envelope, such as a header, version, or marketplace,\naround serialized records.\n\nUse this pattern when each message needs a small number of fields rather than the whole record."},"ignoreEmptyNodes":{"type":"boolean","description":"When true, fields with empty values are stripped from the request body before it is sent,\nso the target API receives only populated fields. Shown as \"Remove empty fields from HTTP\nrequest body\" in the UI."},"existingExtract":{"type":"string","description":"Field name or JSON path that drives the upsert decision for composite imports with both CREATE and UPDATE in requestType.\nWhen the field has a value in the incoming record, the UPDATE operation's relativeURI and method are used; when it is empty or missing, the CREATE operation runs.\nMust match a field populated by an upstream lookup or response mapping (e.g. a destination system ID like \"shopifyCustomerId\"); omit for single-operation imports."},"existingLookupName":{"type":"string","description":"Name of an entry in `lookups` that resolves whether each record already exists in the destination, driving the composite upsert decision when `existingExtract` alone is insufficient (e.g. the check requires a call to the target system). Paired with composite CREATE/UPDATE imports.\n\nUse `existingLookupName` when the incoming record has no destination ID and\nneeds a per-record probe to discover one;\nuse `existingExtract` when the record already carries the ID.\n\nFor single-operation imports,\nuse `ignoreExtract` or `ignoreLookupName` with `ignoreExisting` or\n`ignoreMissing` to skip records instead of routing them.\n\nThe lookup pattern adds one HTTP request per record at runtime,\nmaking it better suited to one-off migrations and low-volume reconciliation than\nhigh-volume recurring syncs."},"ignoreExtract":{"type":"string","description":"Field name on the incoming record that drives the SKIP-vs-PROCESS\ndecision when ``ignoreExisting`` or ``ignoreMissing`` is set.\n\nPairs with either of the two skip-mode flags:\n\n- With ``ignoreExisting: true`` (CREATE-only imports that should\n  avoid duplicates): if ``ignoreExtract`` has a value on the\n  record, the record is treated as already-existing and is\n  skipped; if empty, the record is created.\n- With ``ignoreMissing: true`` (UPDATE-only imports that should\n  avoid creating new records): if ``ignoreExtract`` has a value,\n  the record is treated as existing and is updated; if empty,\n  the record is skipped.\n\n``ignoreExtract`` checks a field that's already on the record —\nzero per-record HTTP overhead.  Use ``ignoreLookupName`` instead\nwhen the record has no field to inspect and the existence check\nneeds a per-record probe against the destination.\n\n**Mutually exclusive with ``ignoreLookupName``**\n\nSet EXACTLY ONE of ``ignoreExtract`` / ``ignoreLookupName`` when\n``ignoreExisting`` or ``ignoreMissing`` is enabled.\n\n**When not to set this field**\n\nOnly valid for single-operation imports with either\n``ignoreExisting: true`` or ``ignoreMissing: true`` set.\nComposite (upsert) imports — those with both ``CREATE`` and\n``UPDATE`` in ``requestType`` — use ``existingExtract`` /\n``existingLookupName`` instead, because they route records\nbetween two write paths rather than skipping them."},"endPointBodyLimit":{"type":"integer","description":"Maximum size limit for the request body in bytes.\nUsed to enforce API-specific size constraints.\n"},"headers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header to include with each import request."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions rendered against the pre-mapped record."}}},"description":"Custom HTTP headers included with import requests; values can contain Handlebars expressions.\nValue templates render against the pre-mapped record (the original input before the Import's mapping step) — use `{{record.<field>}}` to reference fields as they appear in the upstream source."},"response":{"type":"object","properties":{"resourcePath":{"type":"array","items":{"type":"string"},"description":"Per-operation JSON path to the resource collection in each\nresponse.  Outer-array length MUST match ``requestType``\nlength: 1 entry for single-op imports, N entries for an\nN-operation composite import (duplicate the value when\noperations agree).  See the ``response`` object docs\nabove for the full positional-array contract.\n\nRequired when ``batchSize > 1`` because the platform needs\nto know where the array of result records lives inside the\nresponse envelope.  When the API returns a bare array at\nthe top level, leave this field unset.\n\nEach entry is a DOT-PATH STRING (e.g. ``\"data.results\"``,\n``\"items\"``) — do NOT split paths into segments across\narray elements."},"resourceIdPath":{"type":"array","items":{"type":["string","null"]},"description":"Per-operation JSON path to the unique ID field within each\nrecord in the response.  Outer-array length MUST match\n``requestType`` length — duplicate the value when both\noperations agree (the common case) rather than collapsing\nto a 1-entry array.\n\nEach entry is a DOT-PATH STRING, or null for an operation\nwith no ID path configured. When not specified, the platform\nlooks for standard `id` or `_id` fields automatically."},"successPath":{"type":"array","items":{"type":"string"},"description":"Per-operation JSON path to a field that indicates whether\nthe API call succeeded.  Outer-array length MUST match\n``requestType`` length — duplicate the value when both\noperations agree rather than collapsing to a 1-entry\narray on a composite import.\n\nUse this when the API returns HTTP 200 for everything and\nsignals success / failure through a body field.  Pairs with\n``successValues`` to define which values at this path mean\nsuccess.\n\nEach entry is a DOT-PATH STRING."},"successValues":{"type":"array","items":{"type":"array","items":{"type":"string"}},"description":"Per-operation list of values at ``successPath`` that mean\nsuccess.  TWO levels of array nesting:\n\n- Outer array: one entry per ``requestType`` index.\n  Length MUST match ``requestType`` length — duplicate\n  the inner array when both operations agree rather than\n  collapsing to a 1-entry outer array on a composite\n  import.\n- Inner array: the list of acceptable success values for\n  that operation.\n\nAll comparisons are STRING comparisons regardless of the\nactual response value type — the platform stringifies\nnumbers, booleans, etc. before comparing."},"failPath":{"type":"array","items":{"type":"string"},"description":"Per-operation JSON path to a field that signals failure\neven when HTTP returns 200.  Outer-array length MUST\nmatch ``requestType`` length — duplicate when both\noperations agree.  Same array semantics as ``successPath``.\n\nSome APIs use only ``successPath`` (failure = absence of\nsuccess); others publish both an explicit success indicator\nand an explicit failure indicator.  When both are\navailable, set both — the platform evaluates ``successPath``\nfirst and falls through to ``failPath`` only when the\nsuccess check is inconclusive."},"failValues":{"type":"array","items":{"type":"array","items":{"type":"string"}},"description":"Per-operation list of values at ``failPath`` that mean\nfailure.  Same two-level array shape and length contract\nas ``successValues`` — outer length MUST match\n``requestType`` length, duplicate the inner array on\ncomposite imports when both operations agree.\n\nOften used as the inverse of ``successValues`` (e.g.\n``successValues: [[\"true\"]]`` paired with\n``failValues: [[\"false\"]]``)."},"errorPath":{"type":"string","description":"JSON path to the error message inside the response body.\n\n**Note**: this field is NOT a per-operation array — it's a\nsingle string applied to every operation.  Most APIs use\nthe same error-message field shape for both UPDATE and\nCREATE responses, so the platform doesn't expose a per-op\noverride here."},"allowArrayforSuccessPath":{"type":"boolean","description":"When true, allows array values at successPath during success evaluation."},"hasHeader":{"type":"boolean","description":"When true, treats the first record in the response as a header row (for CSV responses)."}},"description":"Configuration for parsing and interpreting HTTP responses returned\nfrom each write request the import issues.\n\n**Critical: positional per-operation arrays**\n\nEvery field inside ``response`` (``resourcePath``, ``resourceIdPath``,\n``successPath``, ``successValues``, ``failPath``, ``failValues``)\nis an ARRAY whose outer length is positionally aligned with\n``requestType`` / ``method`` / ``relativeURI``.  This is the\nSAME positional-alignment contract as those three sibling\narrays — see ``relativeURI`` docs for the full explanation.\n\nEach outer-array index describes how to parse the response from\nthe operation at that same index in ``requestType``:\n\n- **Single-operation import** (``requestType: [\"CREATE\"]``):\n  every response field is a 1-element array.\n  ``successPath: [\"ok\"]`` means \"for the one operation,\n  look at path ``ok`` to detect success\".\n- **Composite upsert** (``requestType: [\"UPDATE\", \"CREATE\"]``):\n  every response field is a 2-element array.  Index 0 parses\n  the UPDATE response, index 1 parses the CREATE response.\n\n**successValues / failValues — array of arrays**\n\n``successValues`` and ``failValues`` carry an extra array level\nbecause each operation can have MULTIPLE acceptable values:\n\n- Outer array: one entry per operation (positional, as above).\n- Inner array: the list of acceptable values for that operation.\n\nExample:\n```json\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"successPath\": [\"ok\", \"ok\"],\n\"successValues\": [[\"true\", \"1\"], [\"true\"]]\n```\nMeans: the UPDATE response is successful when ``ok`` is either\n``\"true\"`` or ``\"1\"``; the CREATE response is successful only\nwhen ``ok`` is ``\"true\"``.\n\n**Always duplicate when operations agree (REQUIRED for composite)**\n\nThe outer-array length MUST match ``requestType`` length on\nevery response field.  When the UPDATE and CREATE responses\nof the same connector look the same (which is the common\ncase — most APIs return the same shape from both endpoints),\nDUPLICATE the entry so the array has the same length as\n``requestType``.  Do NOT collapse to a 1-entry array on a\ncomposite import.\n\n```json\n// ✅ Correct — explicit per-operation entries, even when identical\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data.id\", \"data.id\"],\n\"successPath\": [\"success\", \"success\"],\n\"successValues\": [[\"true\"], [\"true\"]]\n```\n\n```json\n// ❌ Wrong — 1-entry array on a composite import\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data.id\"],\n\"successPath\": [\"success\"],\n\"successValues\": [[\"true\"]]\n```\n\nWhy duplicate even when they agree:\n\n- **UI parity.**  The Celigo UI renders one response-handling\n  form per ``requestType`` index.  A 1-entry array on a\n  composite import leaves the second operation's form panel\n  empty, which looks like a half-built config to the user\n  reviewing the saved import — even if it works at runtime.\n- **Symmetric shape rule.**  ``relativeURI`` / ``method`` /\n  ``requestType`` REQUIRE the entry count to match across\n  all three (the runtime decider uses positional alignment).\n  Making ``response.*`` follow the same \"outer length ==\n  requestType length\" rule means there's ONE shape rule to\n  remember, not two.\n- **Unambiguous intent.**  A 2-entry duplicated array\n  documents in the saved config that the developer\n  considered both operations.  A 1-entry array could mean\n  \"both ops use this\" or could mean \"I forgot to fill in\n  the second op.\"  Duplication eliminates the ambiguity.\n\nWhen operations DIFFER (rare — different response shapes\nper endpoint), set distinct values at each index.  Either\nway the outer length always matches ``requestType``.\n\n**Common llm mistake (DO not do THIS)**\n\nDo NOT split a single dot-path into array segments thinking the\nouter array represents path segments:\n\n```json\n// WRONG — the LLM expressed \"data.id\" as path segments\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data\", \"id\"]\n```\n\nThis is interpreted by the platform as: UPDATE response's id is\nat path ``data``, CREATE response's id is at path ``id`` — almost\nnever what the user wants.  The CORRECT shape is the single\ndot-path expressed as ONE string per operation, duplicated to\nmatch ``requestType`` length:\n\n```json\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data.id\", \"data.id\"]\n```"},"_asyncHelperId":{"type":"string","format":"objectId","description":"Reference to an AsyncHelper resource that polls an asynchronous\ndestination API on this import's behalf.\n\nSet this ONLY when the destination API is genuinely asynchronous — it\nacknowledges the submitted payload (HTTP 202, a job ticket) and ingests\nit in the background, so completion must be polled for (e.g. bulk\nimage/file ingestion, large bulk-load endpoints). Most imports are\nsynchronous and need NO async helper; adding one to a synchronous\ndestination just adds polling overhead plus a status and result export\nto maintain. When in doubt, leave it unset.\n\nThe referenced helper bundles the polling config plus a required status\nexport (polled to check progress) and a result export (fetches the\nfinal payload). An import configured with an async helper cannot carry\nits own transform, output filter, or preSavePage hook — put that\nprocessing in the result export instead."},"ignoreLookupName":{"type":"string","description":"Name of an entry in `lookups` used to check whether each record already exists, so `ignoreMissing`/`ignoreExisting` can skip it. Use it instead of `ignoreExtract` when existence must be resolved by a call to the target system rather than read from a field on the incoming record."},"lookups":{"description":"Lookups referenced by `existingLookupName`/`ignoreLookupName` to resolve record existence against the destination system at runtime.","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia ``lookupName`` and Handlebars expressions reference it via\n``{{lookup.name}}``. Must be unique within this\nresource's ``lookups`` array.\n"},"method":{"type":"string","enum":["GET","POST","PUT","PATCH","DELETE"],"description":"HTTP method used to issue the lookup request. ``GET`` and\n``POST`` are most common. Omit when using a static ``map`` or\n``_lookupCacheId``.\n"},"relativeURI":{"type":"string","description":"HTTP path (relative to the connection's base URL) that\nresolves the lookup. Use Handlebars with triple braces to\ninject values from the incoming record — e.g.\n``/customers?email={{{email}}}`` or\n``/accounts/{{{accountId}}}``.\n"},"postBody":{"type":"string","description":"Request body template for POST / PUT / PATCH lookup methods.\nTypically a JSON string with Handlebars placeholders —\n``{\"email\":\"{{{email}}}\"}``. Ignored for GET / DELETE.\n"},"headers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}},"description":"Optional custom headers to attach to the lookup request.\nUseful for endpoints that need a different Accept header or\nan auxiliary auth token separate from the connection-level\nheaders.\n"},"extract":{"type":"string","description":"JSONPath expression that selects the lookup value from the\nHTTP response body. ``$.`` prefix optional. Examples:\n``$.data[0].id``, ``$.results.primaryKey``, ``id``.\n"},"_lookupCacheId":{"type":"string","format":"objectId","description":"Optional reference to a LookupCache resource. When set, the\nlookup reads from the cache instead of issuing a live HTTP\nrequest — useful for stable reference data that should be\npre-loaded.\n"},"map":{"type":["object","null"],"description":"Optional static key→value object evaluated BEFORE the HTTP\nrequest. When the incoming value matches a key, the mapped\nvalue is returned without hitting the remote endpoint. Null\non dynamic lookups with no static map.\n"},"default":{"type":["string","null"],"description":"Value returned when the HTTP request returns no usable\nmatch. When omitted and ``allowFailures`` is false, an\nunmatched lookup halts the record. Stored as null when no\nfallback is configured.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, a lookup miss (no matching response and no\n``default``) resolves to ``null`` and the import continues.\nWhen false (default), a miss halts the record. May be\nstored as null (treated as unset).\n"},"useDefaultOnMultipleMatches":{"type":"boolean","description":"Controls behaviour when the response contains more than one\nmatch. When true, the ``default`` value is returned instead\nof raising an ambiguous-match error. When false (default),\nmultiple matches are treated as a failure.\n"}}},"type":"array"}}},"Ftp":{"type":"object","description":"Defines where files are written on 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 target folder and the file naming comes from\nthe import's file configuration.","required":["directoryPath"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies partner-specific B2B settings for this import.\nWhen set, the import inherits the connector's pre-configured settings; omit to use only\nthe FTP connection details."},"directoryPath":{"type":"string","description":"Directory on the server where imported files are written, either absolute or relative to\nthe login directory; the FTP user must have write permission on it. Use forward slashes\nregardless of server OS — paths are case-sensitive on UNIX/Linux servers. Supports\nhandlebars templates."},"fileName":{"type":"string","description":"Name of the file written to the server, including its extension; do not include directory\nseparators — the location comes from directoryPath. Supports handlebars placeholders such\nas items-{{timestamp}}.csv to generate a unique name per run."},"inProgressFileName":{"type":"string","description":"Temporary name the file carries while its upload is in progress, preventing other systems\nfrom processing a partially transferred file; it is renamed to its final name once the\nupload completes. Include a handlebars uniqueness token (e.g. {{timestamp}}) when one file\nis written per flow run."},"backupDirectoryPath":{"type":"string","description":"Directory on the same server where a copy of each written file is retained after a\nsuccessful import; if omitted, no server-side backup is kept. Supports static paths or\nhandlebars templates."}}},"Jdbc":{"type":"object","description":"Configuration for JDBC import operations. Defines how data is written to a database\nvia a JDBC connection.\n\n**Query type determines which fields are required**\n\n| queryType        | Required fields              | Do NOT set        |\n|------------------|------------------------------|-------------------|\n| [\"per_record\"]   | query (array of SQL strings) | bulkInsert        |\n| [\"per_page\"]     | query (array of SQL strings) | bulkInsert        |\n| [\"bulk_insert\"]  | bulkInsert object            | query             |\n| [\"bulk_load\"]    | bulkLoad object              | query             |\n\n**Critical:** query IS AN ARRAY OF STRINGS\nThe query field must be an array of plain strings, NOT a single string and NOT an array of objects.\nCorrect: [\"INSERT INTO users (name) VALUES ('{{{name}}}')\"]\nWrong: \"INSERT INTO users ...\"\nWrong: [{\"query\": \"INSERT INTO users ...\"}]","required":["queryType"],"properties":{"query":{"type":"array","items":{"type":"string"},"description":"Array of SQL query strings to execute. REQUIRED when queryType is [\"per_record\"] or [\"per_page\"].\n\nEach element is a complete SQL statement as a plain string. Typically contains a single query.\n\nUse Handlebars {{fieldName}} syntax to inject values from incoming records.\n\n**Format — array of strings**\n- CORRECT: [\"INSERT INTO users (name, email) VALUES ('{{{name}}}', '{{{email}}}')\"]\n- WRONG: \"INSERT INTO users ...\"  (not an array)\n- WRONG: [{\"query\": \"INSERT INTO users ...\"}]  (objects are invalid — causes Cast error)\n\n**Examples**\n- INSERT: [\"INSERT INTO users (name, email) VALUES ('{{{name}}}', '{{{email}}}')\"]\n- UPDATE: [\"UPDATE inventory SET qty = {{{quantity}}} WHERE sku = '{{{sku}}}'\"]\n- MERGE/UPSERT: [\"MERGE INTO target USING (SELECT CAST(? AS VARCHAR) AS email) AS src ON target.email = src.email WHEN MATCHED THEN UPDATE SET name = ? WHEN NOT MATCHED THEN INSERT (name, email) VALUES (?, ?)\"]\n\n**String vs numeric values in handlebars**\n- Use triple braces so the raw value is emitted; double braces emit the value already single-quoted, so wrapping them in quotes double-quotes it.\n- Strings: supply the quotes yourself — '{{{name}}}'\n- Numbers: no quotes — {{{quantity}}}"},"queryType":{"type":"array","items":{"type":"string","enum":["bulk_insert","per_record","per_page","bulk_load"]},"description":"Execution strategy for the SQL operation. REQUIRED. Must be an array with one value.\n\n**Decision tree**\n\n1. If UPDATE or UPSERT/MERGE → [\"per_record\"] (set query field)\n2. If INSERT with \"ignore existing\" / \"skip duplicates\" / match logic → [\"per_record\"] (set query field)\n3. If pure INSERT with no duplicate checking → [\"bulk_insert\"] (set bulkInsert object)\n4. If high-volume bulk load → [\"bulk_load\"] (set bulkLoad object)\n\n**Critical relationship to other fields**\n| queryType        | REQUIRES              | DO NOT SET   |\n|------------------|-----------------------|--------------|\n| [\"per_record\"]   | query (array)         | bulkInsert   |\n| [\"per_page\"]     | query (array)         | bulkInsert   |\n| [\"bulk_insert\"]  | bulkInsert object     | query        |\n| [\"bulk_load\"]    | bulkLoad object       | query        |\n\n**Examples**\n- Per-record upsert: [\"per_record\"]\n- Bulk insert: [\"bulk_insert\"]\n- Bulk load: [\"bulk_load\"]\n"},"bulkInsert":{"type":"object","description":"Bulk insert configuration. REQUIRED when queryType is [\"bulk_insert\"]. DO NOT SET when queryType is [\"per_record\"].\n\nEnables efficient batch insertion of records into a database table without per-record SQL.\n","properties":{"tableName":{"type":"string","description":"Target database table name for bulk insert. REQUIRED.\nCan include schema qualifiers (e.g., \"schema.tableName\").\n"},"batchSize":{"type":"string","description":"Number of records per batch during bulk insert.\nLarger values improve throughput but use more memory.\nCommon values: \"1000\", \"5000\".\n"}}},"bulkLoad":{"type":"object","description":"Bulk load configuration. REQUIRED when queryType is [\"bulk_load\"]. Uses database-native bulk loading for maximum throughput.\n","properties":{"tableName":{"type":"string","description":"Target database table name for bulk load. REQUIRED.\nCan include schema qualifiers (e.g., \"schema.tableName\").\n"},"primaryKeys":{"type":["array","null"],"items":{"type":"string"},"description":"Primary key column names for upsert/merge during bulk load.\nWhen set, existing rows matching these keys are updated; non-matching rows are inserted.\nExample: [\"id\"] or [\"order_id\", \"product_id\"] for composite keys.\n"},"overrideMergeOrInsertQuery":{"type":"boolean","description":"When true, a custom merge/insert query replaces the auto-generated statement for the\nbulk load, enabling ignore-existing logic or conditional updates."}}},"lookups":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia `lookupName` and Handlebars expressions reference it via\n`{{lookup \"name\" value}}`. Must be unique within this\nresource's `lookups` array."},"query":{"type":"string","description":"SQL query for the lookup (e.g., \"SELECT id FROM users WHERE email = '{{{email}}}'\")."},"extract":{"type":"string","description":"JSONPath-style expression that selects the value from the\nquery result row (e.g. `id`, `details.price`). Omit when\nthe query returns a single scalar."},"_lookupCacheId":{"type":"string","format":"objectId","description":"Optional reference to a LookupCache resource. When set, the\nlookup reads from the cache instead of issuing a live SQL\nquery — useful for stable reference data that should be\npre-loaded."},"allowFailures":{"type":"boolean","description":"When true, a lookup miss (no matching row and no `default`)\nresolves to `null` and the import continues. When false\n(default), a miss halts the record."},"map":{"type":"object","description":"Optional static key→value object evaluated BEFORE the SQL\nquery. When the incoming value matches a key, the mapped\nvalue is returned without querying the database."},"default":{"type":"string","description":"Value returned when the query matches no rows. When omitted\nand `allowFailures` is false, an unmatched lookup halts the\nrecord."}}},"description":"Lookup definitions executed against the JDBC connection. Each entry\nruns a `SELECT` and extracts a value used by field mappings\n(referenced via `lookupName`) or Handlebars expressions\n(referenced via `{{lookup \"name\" value}}`)."}}},"Mongodb":{"type":"object","description":"Configuration for MongoDB imports. Contains only the MongoDB-specific properties; import-level fields such as ignoreExisting, ignoreMissing, name, and description are set on the parent import, not here.","required":["method","collection"],"properties":{"method":{"type":"string","enum":["insertMany","updateOne"],"description":"Write operation performed against the MongoDB collection for each record."},"collection":{"type":"string","description":"MongoDB collection that receives the imported records.\nCollection names are case-sensitive and must not start with the reserved \"system.\" prefix."},"filter":{"type":"string","description":"MongoDB query filter that selects the document each updateOne applies to; required when\nmethod is updateOne. Supports standard MongoDB query operators and dot notation, and\nhandlebars placeholders to reference the incoming record's fields."},"document":{"type":"string","description":"Content of the document written to the collection (used when method is insertMany), as a JSON string that can include nested documents and arrays and handlebars placeholders."},"update":{"type":"string","description":"Modifications applied to matching documents (used when method is updateOne), using MongoDB update operators such as $set, $inc, or $push.\nA complete document without operators replaces the matched document entirely."},"upsert":{"type":"boolean","description":"When true, inserts a new document built from the update criteria if no existing document matches the filter.\nWhen false, records with no matching document are skipped and nothing is inserted."},"ignoreExtract":{"type":"string","description":"Path to the field in the source record that identifies existing records. When that field has a value, the record is treated as existing and skipped.\nOnly used when the import-level ignoreExisting flag is true; do not set it otherwise.\nEnclose field names containing special characters in square brackets (for example, [vendor-code]), and reference array items with an index, such as items[0].id."},"ignoreLookupFilter":{"type":"string","description":"JSON-stringified MongoDB query filter used to find existing documents in the target collection when the import-level ignoreExisting flag is true.\nIf the query matches a document, the incoming record is treated as existing and skipped.\nUse Handlebars placeholders to reference values from the incoming record, for example \"{\\\"email\\\":\\\"{{email}}\\\"}\".\nUnlike ignoreExtract, which only checks whether a field on the incoming record has a value, this filter queries the MongoDB collection itself."}},"if":{"properties":{"method":{"const":"updateOne"}},"required":["method"]},"then":{"required":["filter"]}},"NetSuite":{"type":"object","description":"Configuration for NetSuite imports (legacy adaptor; use netsuite_da / NetSuiteDistributedImport for SuiteApp 2.0).","properties":{"lookups":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique name for this lookup, referenced from mappings and handlebars templates with `{{lookup 'name' value}}`."},"recordType":{"type":"string","description":"NetSuite record type searched by a dynamic lookup (for example, `customer`, `salesOrder`). Set with `searchField` or `expression`; omit for a static `map` lookup."},"searchField":{"type":"string","description":"NetSuite field the dynamic lookup searches on (for example, `externalid`). Must be a searchable field on `recordType`. Use this or `expression`."},"expression":{"type":"string","description":"NetSuite search expression evaluated at runtime to select matching records (for example, `[\"recipient\",\"is\",\"123\"]`). Use this or `searchField`."},"resultField":{"type":"string","description":"Field on the matched NetSuite record whose value the lookup returns (for example, `internalid`).\nSupports dot notation for nested fields."},"map":{"type":["object","null"],"description":"Static lookup table with input values as keys and their corresponding output values. Use instead of a NetSuite search for fixed value translations. The platform stores `null` here on search-driven lookups."},"default":{"type":["string","null"],"description":"Fallback value returned when the lookup finds no match. Used together with `allowFailures`. Stored as `null` when not configured."},"allowFailures":{"type":"boolean","description":"When true, an unmatched lookup falls back to `default` and processing continues; when false, an unmatched lookup fails the record."},"_id":{"type":"object","description":"Server-assigned unique identifier for this lookup entry.","readOnly":true}}},"description":"Lookup definitions used to resolve reference values during the import, using either a static `map` or a dynamic NetSuite record search."},"operation":{"type":"string","enum":["add","update","addupdate","attach","detach","delete"],"description":"Operation to perform on NetSuite records, as a plain string (the API lowercases the value).\nFor `update`, `addupdate`, and `delete`, also set internalIdLookup so existing records can be found; for `add` with ignoreExisting, set internalIdLookup to check for duplicates before creating.\nDefault to `addupdate` when the intent is \"sync\", \"upsert\", or \"create or update\"; use `add` for plain \"create\" or \"insert\".\nDo not wrap the value in an object like `{\"type\": \"addupdate\"}` — that fails validation."},"isFileProvider":{"type":"boolean","description":"When true, enables file operations against NetSuite — browsing, uploading, downloading, updating, and deleting files.\nWhen false or omitted, file-related operations are unavailable through this import."},"customFieldMetadata":{"type":"object","description":"Metadata describing the custom fields defined on the target NetSuite record type, used to validate and process custom field data during the import."},"recordType":{"type":"string","description":"NetSuite record type this import writes to (e.g. customer, salesOrder, invoice).\nUse the exact NetSuite internal record type identifier; both standard and custom record types are supported."},"recordTypeId":{"type":"string","description":"The unique identifier specifying the record type within NetSuite. Determines the applicable schema, fields, validation rules, and processing logic. Use exact NetSuite record type identifiers such as standard types or custom record IDs."},"retryUpdateAsAdd":{"type":"boolean","description":"When true, an update that fails because the target record does not exist is automatically retried as an add.\nWhen false or unset, update failures error immediately without retrying.\nMay create duplicate records if the update failed for a reason other than a missing record."},"batchSize":{"type":"number","description":"Number of records sent to NetSuite per API call.\nLarger batches reduce the number of calls but increase memory use and timeout risk per call."},"internalIdLookup":{"type":"object","properties":{"extract":{"type":"string","description":"Path used to extract the value for the internal ID lookup."},"searchField":{"type":"string","description":"The NetSuite record field used as the key attribute for the internal ID lookup search. Should be a unique or indexed field to ensure accurate matches. Must correspond to a valid, searchable field on the target record type."},"expression":{"type":"string","description":"NetSuite search expression that filters which records match the lookup.\nSupports logical operators (AND, OR, NOT), comparison operators, and nested conditions, evaluated at runtime against current data."}},"description":"Configuration for locating existing NetSuite records by internal ID.\nRequired when operation is `update`, `addupdate`, or `delete`; also used with `add` plus ignoreExisting to check for duplicates."},"preferences":{"type":"object","properties":{"ignoreReadOnlyFields":{"type":"boolean","description":"When true, silently skips read-only fields during update operations instead of raising errors."},"warningAsError":{"type":"boolean","description":"When true, treats NetSuite warnings as errors, causing the operation to fail immediately."},"skipCustomMetadataRequests":{"type":"boolean","description":"When true, skips custom metadata requests during NetSuite API operations to improve performance."}},"description":"Preferences that control how NetSuite handles the import operation."},"file":{"type":"object","properties":{"name":{"type":"string","description":"Filename for the file in the NetSuite File Cabinet, without any folder or path information.\nMust be unique within its folder; include the file extension (e.g. .pdf, .csv)."},"fileType":{"type":"string","description":"NetSuite file type that governs how the file is processed, stored, and displayed.\nMust match the actual content format of the file."},"folder":{"type":"string","description":"Folder in the NetSuite File Cabinet where the file is stored, as a numeric folder ID or a folder path.\nUpdating this on an existing file moves the file to the specified folder."},"folderInternalId":{"type":"string","description":"Internal ID of the File Cabinet folder where the file is stored.\nMust reference an existing folder; changing it moves the file to a different folder."},"internalId":{"type":"string","description":"NetSuite internal ID of an existing file, used to target it for retrieval, updates, or deletion.\nAssigned by NetSuite when the file is created and cannot be changed."},"backupFolderInternalId":{"type":"string","description":"Internal ID of the File Cabinet folder where backup files are stored."}},"description":"Configuration for the file to upload to or update in the NetSuite File Cabinet."}},"if":{"not":{"propertyNames":{"enum":["preferences","lookups"]}}},"then":{"required":["operation"],"if":{"properties":{"isFileProvider":{"const":true}},"required":["isFileProvider"]},"else":{"required":["recordType"]}}},"NetsuiteDistributed":{"type":"object","description":"Configuration for NetSuite Distributed (SuiteApp 2.0) import operations — the primary sub-schema for the NetSuiteDistributedImport adaptorType. The API field name is \"netsuite_da\".\noperation and recordType are required. The server may attach empty bookkeeping stubs of\nthis object (empty lookups/mapping, or missingOrCorruptedDAConfig) to imports of other\nadaptor types; stubs omit these fields. Set internalIdLookup so existing records\ncan be found when operation is update, addupdate, or delete.","properties":{"operation":{"type":"string","enum":["add","update","addupdate","attach","detach","delete"],"description":"Operation to perform on the target NetSuite record, as a plain string. Required.\nFor `update`, `addupdate`, and `delete`, also set internalIdLookup so existing records can be found."},"recordType":{"type":"string","description":"NetSuite record type to import into (e.g. \"customer\", \"salesorder\", \"customrecord_myrecord\" for custom record types). Required.\nMust match a valid NetSuite record type identifier."},"recordIdentifier":{"type":"string","description":"Custom record identifier, used to identify the specific record type when\nimporting into custom record types.\n"},"restletVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"Version of the NetSuite RESTlet to use, as a plain string.\nDefaults to \"suiteapp2.0\" when useSS2Restlets is true, \"suitebundle\" otherwise; rarely needs to be set explicitly for modern integrations.\nThe version is fixed when the step is created — the Advanced selector is disabled on existing steps, and migrating an existing step to a different version means recreating or cloning it."},"useSS2Restlets":{"type":"boolean","description":"When true, uses SuiteScript 2.0 RESTlets and restletVersion defaults to \"suiteapp2.0\".\nDefaults to true for modern integrations.\nSet at step creation together with restletVersion — the same creation-time-only constraint applies (see restletVersion)."},"missingOrCorruptedDAConfig":{"type":"boolean","description":"When true, the Distributed Adaptor configuration is missing or corrupted.\nSet by the system — do not set manually."},"batchSize":{"type":"number","description":"Number of records to process per batch. Controls how many records are sent\nto NetSuite in a single API call. Typical values: 50-200.\n"},"internalIdLookup":{"type":"object","description":"Configuration for looking up existing NetSuite records to match against incoming data.\nRequired when operation is \"update\", \"addupdate\", or \"delete\".","properties":{"extract":{"type":"string","description":"Path in the source record to extract the lookup value from (e.g. \"internalId\" or \"externalId\")."},"searchField":{"type":"string","description":"NetSuite field to search against (e.g. \"externalId\", \"email\", \"name\", \"tranId\")."},"operator":{"type":"string","description":"Comparison operator for the lookup (e.g. \"is\", \"contains\", \"startswith\")."},"expression":{"type":"string","description":"NetSuite search expression for complex lookup conditions, used for multi-field or conditional lookups."}}},"hooks":{"type":"object","description":"Script hooks for custom processing at different stages of the import.\nEach hook references a SuiteScript file and function.\n","properties":{"preMap":{"type":"object","description":"Runs before field mapping is applied.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}},"postMap":{"type":"object","description":"Runs after field mapping, before submission to NetSuite.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}},"postSubmit":{"type":"object","description":"Runs after the record is submitted to NetSuite.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}}}},"mapping":{"type":"object","description":"NetSuite-specific Mapper 1.0 field mappings (the \"DA\" dialect).\nUsed by `NetSuiteDistributedImport` exclusively — NetSuite's\nDistributed Adaptor cannot consume generic v1 mappings or the\nMapper 2.0 shape because it needs sublist, subrecord, and\nconditional lookup semantics the generic schemas don't model.\n\nField-level shape: `fields` for body columns, `lists` for\nsublists / line-item arrays.","properties":{"fields":{"type":"array","items":{"type":"object","properties":{"extract":{"type":["string","null"],"description":"Path in the source record to extract the value from.\nUse dot notation for nested fields (e.g., \"address.city\").\nThe platform stores `null` here on fields driven by\n`hardCodedValue` instead of an extract path.\n"},"generate":{"type":"string","description":"The NetSuite field ID to write the value to (e.g., \"companyname\", \"email\", \"subsidiary\").\n"},"hardCodedValue":{"type":"string","description":"A static value to always use instead of extracting from source data.\nMutually exclusive with extract.\n"},"lookupName":{"type":"string","description":"Reference to a lookup defined in netsuite_da.lookups by name."},"dataType":{"type":"string","description":"Data type hint for the field value (e.g., \"string\", \"number\", \"date\", \"boolean\").\n"},"internalId":{"type":"boolean","description":"When true, the value is a NetSuite internal ID reference."},"immutable":{"type":"boolean","description":"When true, this field is only set on record creation, not on updates."},"discardIfEmpty":{"type":"boolean","description":"When true, skip this field mapping if the extracted value is empty."},"extractDateFormat":{"type":"string","description":"Date format of the extracted value (e.g., \"MM/DD/YYYY\", \"ISO8601\").\nUsed to parse date strings from source data.\n"},"extractDateTimezone":{"type":["string","null"],"description":"Timezone of the extracted date value (e.g., \"America/New_York\"). Stored as `null` when not configured."},"subRecordMapping":{"type":"object","description":"Nested Mapper 1.0 mapping for a NetSuite subrecord\nembedded in this field.  A subrecord is a child\nrecord that only exists inside a parent (e.g. the\nInventory Detail on a serialized-item line of a\nsales order); NetSuite reaches it through the\nparent.\n\n**Recursive structure.**  The nested `mapping`\nobject has the same `{fields, lists}` shape as the\ntop-level `netsuite_da.mapping`.  Recursion is\nallowed but rare.","properties":{"recordType":{"type":"string","description":"NetSuite's internal record-type id of the\nSUBRECORD (not the parent).  E.g.\n`inventorydetail`, `itempricing`,\n`mainaddress`.\n"},"jsonPath":{"type":"string","description":"JSONPath into the source record that selects the\ndata feeding this subrecord.  Semantics depend\non placement:\n* Line-level (on `lists[].fields[].`\n  `subRecordMapping`) — often `$` to reuse\n  the enclosing sublist line's row, or a path\n  into a nested array on that row.\n* Body-level (on `fields[].subRecordMapping`)\n  — a path into the source record root.\nDefaults to `$`.\n"},"mapping":{"type":"object","description":"Nested `{fields, lists}` mapping for the\nsubrecord.  Identical shape to the top-level\n`netsuite_da.mapping`.  Subrecords often have\nempty `fields` and only a sublist (e.g.\n`inventorydetail` → `inventoryassignment`).\n"},"lookups":{"type":"array","description":"Subrecord-scoped lookups.  Same shape as the\ntop-level `netsuite_da.lookups`.  Fields inside\nthis `subRecordMapping.mapping` reference them\nvia `lookupName`.  Often `[]`.\n"},"referenceFieldId":{"type":"string","description":"Secondary reference-field identifier used on a\nsmall fraction of subrecord blocks in production\n(~3%).  Exact semantics are not publicly\ndocumented by Celigo — match the value on\nexisting resources when present; do NOT\nsynthesize one.  Leave unset unless a template\nclearly requires it.\n"}}},"conditional":{"type":"object","description":"Conditional logic for when to apply this field mapping\n(\"Only perform mapping when\" in the UI). Lookup-based\nconditions evaluate the lookup named by `lookupName`,\nwhich must be defined on this import.\n","properties":{"lookupName":{"type":"string","description":"Lookup to evaluate for the condition. Must exactly match the\n`name` of a lookup defined on this import."},"when":{"type":"string","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","ignore_if_set"],"description":"Condition that controls when this field mapping is applied."}}}}},"description":"Body-level field mappings. Each entry maps a source field to a NetSuite body field.\n"},"lists":{"type":"array","items":{"type":"object","properties":{"generate":{"type":"string","description":"The NetSuite sublist ID (e.g., \"item\" for sales order line items,\n\"addressbook\" for address sublists).\n"},"jsonPath":{"type":"string","description":"LEGACY — do not set. Nominally the JSON path to the array of sublist records, but NetSuite DA ignores a top-level jsonPath here."},"fields":{"type":"array","items":{"type":"object","properties":{"extract":{"type":["string","null"],"description":"Path from the source record root that produces\nthe value for this sublist column. Paths are\nalways anchored at the source record root —\nnever relative to a pre-iterated row. The\nplatform stores `null` here on fields driven by\n`hardCodedValue` instead of an extract path."},"generate":{"type":"string","description":"NetSuite sublist field ID to write to."},"hardCodedValue":{"type":"string","description":"Static value for this sublist field."},"lookupName":{"type":"string","description":"Reference to a lookup by name."},"dataType":{"type":"string","description":"Data type hint for the field value."},"internalId":{"type":"boolean","description":"When true, the value is a NetSuite internal ID reference."},"isKey":{"type":"boolean","description":"When true, this field is a key field for matching existing sublist lines."},"immutable":{"type":"boolean","description":"When true, this field is only set on record creation, not on updates."},"discardIfEmpty":{"type":"boolean","description":"When true, skips this field mapping if the extracted value is empty."},"extractDateFormat":{"type":"string","description":"Date format of the extracted value."},"extractDateTimezone":{"type":["string","null"],"description":"Timezone of the extracted date value. Stored as `null` when not configured."},"subRecordMapping":{"type":"object","description":"Nested Mapper 1.0 mapping for a NetSuite\nsubrecord embedded in this sublist-field.  A\nsubrecord is a child record that only exists\ninside a parent (e.g. the Inventory Detail on\na serialized-item line of a sales order);\nNetSuite reaches it through the parent.\n\n**Recursive structure.**  The nested\n`mapping` object has the same\n`{fields, lists}` shape as the top-level\n`netsuite_da.mapping`.  Recursion is allowed\nbut rare.","properties":{"recordType":{"type":"string","description":"NetSuite's internal record-type id of the\nSUBRECORD (not the parent).  E.g.\n`inventorydetail`, `itempricing`,\n`componentinventorydetail`.\n"},"jsonPath":{"type":"string","description":"JSONPath (relative to the current sublist\nline) to the rows that populate the\nsubrecord.  Typically `$` when the\nsubrecord data is carried on the same\nline item (the enclosing\n`lists[].jsonPath` has already selected\nthe line).  Defaults to `$`.\n"},"mapping":{"type":"object","description":"Nested `{fields, lists}` mapping for the\nsubrecord.  Identical shape to the\ntop-level `netsuite_da.mapping`.\nSubrecords often have empty `fields` and\nonly a sublist (e.g. `inventorydetail` →\n`inventoryassignment`).\n"},"lookups":{"type":"array","description":"Subrecord-scoped lookups.  Same shape as\nthe top-level `netsuite_da.lookups`.\nFields inside this\n`subRecordMapping.mapping` reference\nthem via `lookupName`.  Often `[]`.\n"},"referenceFieldId":{"type":"string","description":"Secondary reference-field identifier used\non a small fraction of subrecord blocks\nin production (~3%).  Exact semantics are\nnot publicly documented by Celigo — match\nthe value on existing resources when\npresent; do NOT synthesize one.  Leave\nunset unless a template clearly requires\nit.\n"}}},"conditional":{"type":"object","description":"Conditional logic for when to apply this sublist field mapping\n(\"Only perform mapping when\" in the UI). Lookup-based conditions\nevaluate the lookup named by `lookupName`, which must be defined\non this import.","properties":{"lookupName":{"type":"string","description":"Lookup to evaluate for the condition. Must exactly match the\n`name` of a lookup defined on this import."},"when":{"type":"string","description":"Condition that controls when this field mapping is applied.","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","ignore_if_set"]}}}}},"description":"Field mappings for each column in the sublist."}}},"description":"Sublist (line-item) mappings. Each entry maps source data to a NetSuite sublist.\n"}}},"lookups":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia `lookupName` and Handlebars expressions reference it via\n`{{lookup \"name\" value}}`. Must be unique within this\nresource's `lookups` array."},"recordType":{"type":"string","description":"NetSuite record type to search. Use the internal record-type\nid (lowercase, underscored), e.g. `customer`, `item`,\n`salesorder`, `customlist_xxx`."},"searchField":{"type":"string","description":"NetSuite field on `recordType` to match against. Use the\ninternal field id — e.g. `email`, `externalid`,\n`internalid`, `entityid`, `custitem_xxx`."},"resultField":{"type":"string","description":"NetSuite field whose value is returned when a record matches.\nTypically `internalid` when the lookup resolves to a record\nreference, or any queryable scalar field."},"expression":{"type":"string","description":"Optional advanced-search expression (NetSuite saved-search\nDSL) for multi-criterion lookups. When present, supersedes\n`searchField` / `operator` — those fields are ignored."},"operator":{"type":"string","description":"Comparison operator for the `searchField` match. Common\nvalues: `is`, `contains`, `startswith`, `anyof`,\n`equalto`, `greaterthan`."},"includeInactive":{"type":"boolean","description":"When true, includes inactive records in lookup results."},"useDefaultOnMultipleMatches":{"type":"boolean","description":"Controls behaviour when the search matches more than one\nrecord. When true, the `default` value is returned instead\nof raising an ambiguous-match error. When false (default),\nmultiple matches are treated as a failure."},"allowFailures":{"type":["boolean","null"],"description":"When true, a lookup miss (no matching record and no\n`default`) resolves to `null` and the import continues.\nWhen false (default), a miss halts the record. May be\nstored as null (treated as unset)."},"map":{"type":["object","null"],"description":"Optional static key→value object evaluated BEFORE the\nNetSuite search. When the incoming value matches a key, the\nmapped value is returned without hitting NetSuite. Null on\ndynamic (search-based) lookups with no static map."},"default":{"type":["string","null"],"description":"Value returned when the search matches no records. When\nomitted and `allowFailures` is false, an unmatched lookup\nhalts the record. May be stored as null (no default)."}}},"description":"Lookup definitions that resolve reference values from NetSuite.\nReferenced by name from field mappings via `lookupName` and from\nHandlebars expressions via `{{lookup \"name\" value}}`."},"rawOverride":{"type":"object","description":"Raw override object for advanced use cases. When useRawOverride is true,\nthis object is sent directly to the NetSuite API, bypassing normal mapping.\n"},"useRawOverride":{"type":"boolean","description":"When true, uses rawOverride instead of the normal mapping configuration.\n"},"isMigrated":{"type":"boolean","description":"When true, this import was migrated from a legacy format. Set by the system."},"retryUpdateAsAdd":{"type":"boolean","description":"When true, if an update fails because the record doesn't exist, automatically\nretry as an add operation. Useful for initial syncs where records may not exist yet.\n"},"customFieldMetadata":{"type":"object","description":"Metadata about custom fields on the target record type.\nPopulated by the system from NetSuite metadata — do not set manually.\n"},"file":{"type":"object","description":"File cabinet configuration for file-based imports into NetSuite.\n","properties":{"name":{"type":"string","description":"Filename for the file in NetSuite File Cabinet."},"fileType":{"type":"string","description":"NetSuite file type (e.g., \"PDF\", \"CSV\", \"PLAINTEXT\", \"EXCEL\", \"XML\").\n"},"folder":{"type":"string","description":"Folder path or name in the NetSuite File Cabinet."},"folderInternalId":{"type":"string","description":"Internal ID of the target folder in NetSuite File Cabinet."},"internalId":{"type":"string","description":"Internal ID of an existing file to update."},"backupFolderInternalId":{"type":"string","description":"Internal ID of a backup folder for file versioning."}}},"isFileProvider":{"type":"boolean","description":"Whether this import handles files in the NetSuite File Cabinet.\n"},"preferences":{"type":"object","properties":{"ignoreReadOnlyFields":{"type":"boolean","description":"When true, silently skip read-only fields instead of raising errors."},"warningAsError":{"type":"boolean","description":"When true, treat NetSuite warnings as errors that stop the import."},"skipCustomMetadataRequests":{"type":"boolean","description":"When true, skip fetching custom field metadata to improve performance."}},"description":"Import behavior preferences that control how NetSuite handles the import operation.\n"},"recordTypeId":{"type":"string","description":"The internal record type ID. Used for custom record types in NetSuite where\nthe numeric ID is needed in addition to the recordType string.\n"}},"if":{"not":{"propertyNames":{"enum":["lookups","mapping","missingOrCorruptedDAConfig"]}}},"then":{"required":["operation","recordType"],"if":{"properties":{"operation":{"enum":["update","addupdate","delete"]}},"required":["operation"]},"then":{"required":["internalIdLookup"]}}},"Rdbms":{"type":"object","description":"Configuration for RDBMS imports into SQL Server, MySQL, PostgreSQL, Snowflake, Oracle, MariaDB, and other relational databases.\nqueryType determines which companion field is required: per_record, per_page, and first_page use the query field; bulk_insert uses the bulkInsert object; bulk_load uses the bulkLoad object — never set query and bulkInsert together.\n(The server itself stores an empty `query: []` next to bulk_insert/bulk_load configs and may leave an empty `bulkInsert: {}` stub next to others — empty companions are normal in responses; only a populated conflicting companion is invalid.)\nThe query field is an array of plain SQL strings, not a single string and not an array of objects.","properties":{"lookups":{"$ref":"#/components/schemas/Lookups","description":"Lookup definitions executed against the RDBMS connection. Each\nentry runs a `SELECT` and extracts a value used by field mappings\n(referenced via `lookupName`) or Handlebars expressions\n(referenced via `{{lookup \"name\" value}}`).\n\n**Mirror invariant**\nThe Celigo platform persists lookups in TWO locations on an RDBMS\nimport: top-level `lookups` and `rdbms.lookups`. Both arrays\nmust hold byte-for-byte identical content — the MappingsAgent\nwrites both on every save.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia `lookupName` and Handlebars expressions reference it via\n`{{lookup \"name\" value}}`. Must be unique within this\nresource's `lookups` array.\n"},"query":{"type":"string","description":"SQL `SELECT` statement that resolves the lookup value.\nTypically returns a single row with a single column; use\n`extract` to pick the column when the query returns multiple.\nInject values from the incoming record with Handlebars — RDBMS\nqueries use triple braces and the `record.` prefix:\n`SELECT id FROM users WHERE email = '{{{record.email}}}'`.\n"},"extract":{"type":"string","description":"JSONPath-style expression that selects the value from the query\nresult row (e.g. `id`, `details.price`). Omit when the\nquery returns a single scalar — the first column is used\nautomatically.\n"},"_lookupCacheId":{"type":"string","format":"objectId","description":"Optional reference to a LookupCache resource. When set, the\nlookup reads from the cache instead of issuing a live SQL\nquery — useful for stable reference data that should be\npre-loaded.\n"},"map":{"type":"object","description":"Optional static key→value object evaluated BEFORE the SQL query.\nWhen the incoming value matches a key, the mapped value is\nreturned without querying the database. Useful for small\ncontrolled enumerations.\n"},"default":{"type":"string","description":"Value returned when the query matches no rows. When omitted and\n`allowFailures` is false, an unmatched lookup halts the\nrecord.\n"},"allowFailures":{"type":"boolean","description":"When true, a lookup miss (no matching row and no `default`)\nresolves to `null` and the import continues. When false\n(default), a miss halts the record.\n"}}},"type":"array"},"query":{"type":"array","items":{"type":"string"},"description":"SQL statements to execute; required when queryType is `[\"per_record\"]`, `[\"per_page\"]`, or `[\"first_page\"]`, and typically contains a single statement.\nEach element is a complete SQL statement as a plain string — never a single bare string and never an array of objects (objects cause a Cast error).\nInject values from the incoming record with Handlebars using the `record.` prefix and triple braces, e.g. `'{{{record.name}}}'` for strings (single-quoted in the SQL) and `{{{record.quantity}}}` for numbers.\nRendering is strict: a reference the render model cannot resolve (a bare `{{name}}` without prefix, or `record.*` when the arriving record is a grouped array) fails the step with `cannot_evaluate_handlebars` — it does not render an empty value.\n`record` and `rows` are aliases chosen by the arriving data's shape: an object record binds `record` (`rows` undefined); a grouped array-record (e.g. upstream `groupByFields`) binds `rows` (`record` undefined) — address grouped rows as `{{rows.0.field}}` or iterate `{{#each rows}}`.\nDouble braces (`{{record.field}}`) output the value wrapped in single quotes with embedded quotes doubled, while triple braces output it raw; block helpers such as `{{#each}}` and `{{#if}}` use double braces as normal."},"queryType":{"type":["array","null"],"items":{"type":"string","enum":["INSERT","UPDATE","bulk_insert","per_record","per_page","first_page","bulk_load"]},"description":"Execution strategy for the SQL operation, which determines the required companion field: per_record, per_page, and first_page require `query`; bulk_insert requires `bulkInsert`; bulk_load requires `bulkLoad`.\nPrefer the highest-performance type the operation supports: bulk_load (currently Snowflake and NSAW — handles INSERT, upsert via `bulkLoad.primaryKeys`, and custom merge or ignore-existing logic via `bulkLoad.overrideMergeQuery`), then bulk_insert for pure INSERTs, then per_page, then per_record only when the logic cannot be expressed as a batch operation.\nFor UPDATE, UPSERT, or ignore-existing logic on databases without bulk_load support, use per_page or per_record with `query` — bulk_insert has no duplicate-checking logic.\nSet a single value; do not combine types or use the legacy INSERT and UPDATE values.\nper_page statements render against the whole batch (`batch_of_records`) rather than a single record — loop with `{{#each batch_of_records}}...{{{record.fieldName}}}...{{/each}}`.\n\n`bulk_load` — Stages data as a file and loads via database-native COPY/bulk mechanism for maximum throughput; supports INSERT, upsert via bulkLoad.primaryKeys, and custom merge or ignore-existing logic via bulkLoad.overrideMergeQuery; currently supported for Snowflake and NSAW.\n`bulk_insert` — Batch INSERT via multi-row VALUES clause for efficient bulk data loading; has no upsert or ignore-existing logic and is available for all RDBMS types.\n`per_page` — Executes one SQL statement per page (batch) of records; available for all RDBMS types.\n`per_record` — Executes one SQL statement per record, giving full control over individual record SQL; available for all RDBMS types."},"bulkInsert":{"type":"object","description":"Configuration for batch INSERTs via a multi-row VALUES clause; required when queryType is `[\"bulk_insert\"]` and must not be set otherwise.\nOnly suited to pure INSERTs — for UPDATE, UPSERT, or ignore-existing/skip-duplicates logic, use queryType `[\"per_record\"]` with the `query` field instead.","properties":{"tableName":{"type":"string","description":"The name of the database table into which the bulk insert operation will be executed. This value must correspond to a valid, existing table within the target relational database management system (RDBMS). It serves as the primary destination for inserting multiple rows of data efficiently in a single operation. The table name can include schema or namespace qualifiers if supported by the database (e.g., \"schemaName.tableName\"), allowing precise targeting within complex database structures. Proper validation and sanitization of this value are essential to ensure the operation's success and to prevent SQL injection or other security vulnerabilities."},"batchSize":{"type":"string","description":"The number of records to be inserted into the database in a single batch during a bulk insert operation. This parameter is crucial for optimizing the performance and efficiency of bulk data loading by controlling how many records are grouped together before being sent to the database. Proper tuning of batchSize balances memory consumption, transaction overhead, and throughput, enabling the system to handle large volumes of data efficiently without overwhelming resources or causing timeouts. Adjusting batchSize directly impacts transaction size, network utilization, error handling granularity, and recovery strategies, making it essential to tailor this value based on the specific database capabilities, system resources, and workload characteristics."}}},"bulkLoad":{"type":"object","properties":{"tableName":{"type":"string","description":"Target table for the bulk load.\nMust reference an existing table accessible with the connection's credentials; schema-qualified names are supported."},"primaryKeys":{"type":"array","items":{"type":"string"},"description":"Columns that uniquely identify each record in the target table, used to match existing rows during the load.\nSet for upsert behavior (a MERGE is auto-generated); omit (or empty) for a pure INSERT.\nComposite keys are supported — list every key column, and names must exactly match the target schema."},"overrideMergeQuery":{"type":"boolean","description":"When true, a custom merge query replaces the auto-generated MERGE statement, enabling ignore-existing logic, conditional updates, or multi-table operations.\nThe override SQL references `{{import.rdbms.bulkLoad.preMergeTemporaryTable}}` for the staging table."}},"description":"Configuration for bulk loading: data is staged as a file and loaded via the database's native COPY/bulk mechanism for maximum throughput.\nRequired when queryType is `[\"bulk_load\"]`; currently supported for Snowflake and NSAW."},"updateLookupName":{"type":["string","null"],"description":"Name of the lookup used for update operations in the legacy composite (queryType1) flow."},"updateExtract":{"type":["string","null"],"description":"Path used to extract the value that drives update operations in the legacy composite flow."},"ignoreLookupName":{"type":["string","null"],"description":"Name of the lookup used to determine which records to ignore when ignore-existing is enabled."},"ignoreExtract":{"type":["string","null"],"description":"Path used to extract the value that determines whether a record is ignored when ignore-existing is enabled."}},"if":{"not":{"propertyNames":{"enum":["lookups"]}}},"then":{"required":["queryType"],"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"enum":["per_record","per_page"]}}}},"then":{"required":["query"],"properties":{"bulkInsert":{"maxProperties":0}}},"else":{"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"const":"first_page"}}}},"then":{"required":["query"]},"else":{"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"const":"bulk_insert"}}}},"then":{"required":["bulkInsert"],"properties":{"query":{"maxItems":0}}},"else":{"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"const":"bulk_load"}}}},"then":{"required":["bulkLoad"],"properties":{"query":{"maxItems":0}}}}}}}},"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 set to true\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\nIf allowFailures is false and no default is specified, the flow will fail\nwhen encountering unmapped values.\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\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\nThe best practice is typically to set allowFailures to true with a meaningful\ndefault value, so flows remain operational while alerting you to missing mappings.\n"}}}},"S3":{"type":"object","description":"Defines where files are written to 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 destination, fileKey sets the object key, and backupBucket\nretains a copy after a successful import.","required":["region","bucket"],"properties":{"region":{"type":"string","default":"us-east-1","description":"AWS region where the S3 bucket resides. Must match the bucket's actual location to avoid\nconnectivity errors and misrouted requests."},"bucket":{"type":"string","description":"S3 bucket that receives the imported files. The bucket must already exist and the\nconnection's AWS credentials must have s3:PutObject permission on it."},"fileKey":{"type":"string","description":"Object key under which each file is stored in the bucket. Include slashes to organize\nfiles in a folder-style hierarchy (for example, imports/2023/12/orders.json); keys are\ncase-sensitive. Supports handlebars placeholders such as orders-{{timestamp}}.json to\ngenerate a unique key per run."},"backupBucket":{"type":"string","description":"S3 bucket where a copy of each imported file is retained after a successful import; if\nomitted, no backup copy is kept. Must reference an existing bucket the connection's\ncredentials have s3:PutObject permission on."},"serverSideEncryptionType":{"type":"string","description":"Server-side encryption applied to uploaded objects. The connection form sets `AES256`\n(SSE-S3, S3-managed keys) when encryption is enabled; leave unset to use the bucket's\ndefault encryption."}}},"Wrapper":{"type":"object","description":"Configuration for Wrapper imports, which delegate writing records 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.","properties":{"function":{"type":"string","description":"Name of the function the wrapper invokes to process records.\nMust match a callable function in the wrapper's execution context; names are case-sensitive."},"configuration":{"type":"object","additionalProperties":true,"description":"Free-form settings passed to the wrapper function at runtime (connector-specific keys such\nas method, apiVersion, headers, or handler). Structure is defined by the wrapper code, not\nby this schema."},"lookups":{"$ref":"#/components/schemas/Lookups","description":"Lookup definitions for this wrapper import. Wrapper lookups use\nthe generic Celigo shape: either a static `map` (key→value\nobject) or a dynamic `_lookupCacheId` + `extract` pair. See\n`common/schemas/lookups.yml` for the full item shape."}},"if":{"not":{"propertyNames":{"enum":["lookups"]}}},"then":{"required":["function"]}},"Salesforce":{"type":"object","description":"Salesforce-specific configuration for the import: the operation to perform, the API to use, and object-level settings such as `sObjectType`, `idLookup`, and `upsert`. Properties like `ignoreExisting`, `ignoreMissing`, `name`, and `description` are not part of this object — they belong at the import level.","properties":{"lookups":{"description":"Lookup definitions used to resolve Salesforce record references or\nstatic value mappings. Each lookup has a unique ``name`` that mapping\nfields reference via ``lookupName`` and that Handlebars expressions\nreference via ``{{lookup \"name\" value}}``.\n\n**Mirror invariant**\nThe Celigo platform stores lookups in TWO locations on a Salesforce\nimport resource: top-level ``lookups`` and ``salesforce.lookups``.\nBoth arrays must hold byte-for-byte identical content — the\nMappingsAgent writes both on every save.\n\n**Static vs dynamic lookups**\n- **Static** — only ``map`` + optional ``default``. No Salesforce\n  query runs. Use for small controlled vocabularies (country codes,\n  status labels, etc.).\n- **Dynamic** — ``sObjectType`` + ``resultField`` + ``whereClause``.\n  Issues a SOQL query at runtime. Use ``map`` alongside the query to\n  short-circuit known values before hitting Salesforce.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup entry. Mapping fields reference\nit via ``lookupName``, and Handlebars expressions reference it\nvia ``{{lookup \"name\" value}}``. Must be unique within this\nresource's ``lookups`` array. Conventionally a short slug or a\ngenerated hash — the value is only meaningful as a reference\ntoken.\n"},"sObjectType":{"type":"string","description":"Salesforce sObject API name to query (e.g. ``Account``,\n``Contact``, ``Solution__c``). For custom objects include the\n``__c`` suffix. Case-sensitive — must match exactly as declared\nin the target Salesforce org.\n"},"resultField":{"type":"string","description":"sObject field whose value is returned when the SOQL ``whereClause``\nmatches a record. Typically ``Id`` when the lookup resolves to a\nSalesforce record reference, but any queryable field is valid.\n"},"whereClause":{"type":"string","description":"SOQL ``WHERE`` clause that identifies the matching Salesforce\nrecord. Use Handlebars expressions (triple braces recommended:\n``{{{field}}}``) to inject values from the incoming source record.\nWrap the whole expression in parentheses.\n\nExamples:\n- ``(Id = {{{id [Customer SFDC ID]}}})``\n- ``(Email = '{{{email}}}')``\n- ``(External_Id__c = {{{string externalId}}} AND IsActive = true)``\n\nCeligo's Handlebars helpers (``string``, ``id``, ``double``,\n``bool``, ``date``) produce correctly-typed SOQL literals —\nprefer them over manual quoting. String values wrap in single\nquotes; numeric/id values do not.\n"},"map":{"type":["object","null"],"description":"Optional static key→value map evaluated BEFORE the SOQL query.\nWhen the incoming field value matches a key, the mapped value\nis returned without hitting Salesforce. Useful for small\ncontrolled enumerations where a full query would be overkill.\n"},"default":{"type":["string","null"],"description":"Value returned when the SOQL query matches no records. When\nomitted and ``allowFailures`` is false, an unmatched lookup\nfails the record.\n"},"allowFailures":{"type":"boolean","description":"When true, a lookup miss (no matching record and no ``default``)\nis tolerated — the mapped field resolves to ``null`` and the\nimport continues. When false (default), a miss halts processing\nfor the record.\n"},"useDefaultOnMultipleMatches":{"type":"boolean","description":"Controls behaviour when the SOQL query matches more than one\nrecord. When true, the ``default`` value is returned instead of\nraising an ambiguous-match error. When false (default), multiple\nmatches are treated as a failure.\n"}}},"type":"array"},"operation":{"type":"string","enum":["insert","update","upsert","upsertpicklistvalues","delete","addupdate"],"description":"Controls how records are written to Salesforce. Each value pairs with a lookup strategy: `upsert` requires `upsert.externalIdField` plus `idLookup.extract`, while `update`, `delete`, `addupdate`, and `insert` with `ignoreExisting` require `idLookup.whereClause`. Values are converted to lowercase automatically."},"api":{"type":"string","enum":["soap","rest","metadata","compositerecord"],"description":"Selects the Salesforce API used to execute the import. Use `soap` for most imports and another API only when its specific capability is needed; default to `soap` when no API is specified. Values are converted to lowercase automatically."},"soap":{"type":"object","properties":{"headers":{"type":"object","properties":{"allOrNone":{"type":"boolean","description":"When true, the batch is processed atomically: if any record fails, the entire transaction is rolled back and nothing is committed. When false or omitted, records that succeed are committed even if others in the batch fail."}},"description":"SOAP headers included in requests sent to Salesforce, such as the `allOrNone` transaction-control header."},"batchSize":{"type":"number","description":"Number of records sent to Salesforce in each SOAP API batch. Larger batches reduce the number of API calls; smaller batches reduce per-call load and the chance of timeouts."}},"description":"Settings that apply when `api` is `soap`, including SOAP request headers and batch size."},"sObjectType":{"type":"string","description":"Salesforce object the import writes to, such as `Account`, `Contact`, or a custom object like `Vendor__c`. Must exactly match the object's case-sensitive API name; custom objects include the `__c` suffix."},"idLookup":{"type":"object","properties":{"extract":{"type":"string","description":"Field in the incoming data whose value is matched against the Salesforce External ID field named in `upsert.externalIdField`. Set only when `operation` is `upsert`; all other operations use `whereClause` instead. Choose a field that is unique and always populated — missing or null values cause the upsert to fail. If both `extract` and `whereClause` are set, `extract` takes precedence for upsert."},"whereClause":{"type":"string","description":"SOQL condition (without the `WHERE` keyword) that finds existing Salesforce records for each incoming record. Required when `operation` is `update`, `delete`, or `addupdate`, and for `insert` with `ignoreExisting`; for `upsert`, use `extract` instead. Reference incoming fields with triple-brace Handlebars and wrap string values in single quotes (e.g. `Email = '{{{Email}}}'`) — triple braces emit the raw value without HTML-escaping, which would corrupt characters like `&` or `'`. Combine conditions with `AND`/`OR`."}},"description":"Controls how existing Salesforce records are matched before writing. Set exactly one of `extract` (for `upsert`) or `whereClause` (for `update`, `delete`, `addupdate`, and `insert` with `ignoreExisting`); omit for plain inserts.\n\nRequired when operation is addupdate, delete, update, or upsert."},"upsert":{"type":"object","properties":{"externalIdField":{"type":"string","description":"API name of the Salesforce field, marked as External ID, that upsert operations match against. This is the target side of the match; `idLookup.extract` names the source field in the incoming data. If the value matches multiple Salesforce records, the upsert fails."}},"description":"Upsert matching configuration, required when `operation` is `upsert` and omitted for all other operations. Contains only `externalIdField`, which pairs with `idLookup.extract` at the parent level.\n\nRequired when operation is upsert."},"upsertpicklistvalues":{"type":"object","properties":{"type":{"type":"string","enum":["picklist","multipicklist"],"description":"Picklist field type being managed. Values are converted to lowercase automatically."},"fullName":{"type":"string","description":"Fully qualified API name of the picklist field, in `ObjectName.FieldName__c` form (for example, `Account.MyPicklist__c`). Must match the case-sensitive Salesforce API name."},"label":{"type":"string","description":"Display label for the picklist field in the Salesforce UI. Can contain spaces and does not need to match the API name."},"visibleLines":{"type":"number","description":"Number of values visible without scrolling in the Salesforce UI selection box. Only applies when `type` is `multipicklist`."}},"description":"Picklist field definition managed when `operation` is `upsertpicklistvalues`; omit for all other operations. Manages the field's metadata — name, label, and display settings — and requires Salesforce Metadata API permissions.\n\nRequired when operation is upsertpicklistvalues."},"removeNonSubmittableFields":{"type":"boolean","description":"When true, strips fields Salesforce will not accept — read-only, system-generated, and formula fields — from the payload before submission, preventing rejection errors. When false or omitted, all fields are submitted as-is. Only the outgoing payload is affected; the source data is unchanged."},"document":{"type":"object","properties":{"id":{"type":"string","description":"Salesforce-assigned ID of the Document record. The 15-character form is case-sensitive; the 18-character form adds a case-insensitivity checksum."},"name":{"type":"string","description":"Display name of the document in Salesforce, used to identify it in folders, search, and API operations."},"folderId":{"type":"string","description":"Salesforce folder that stores the document. The document inherits the folder's sharing and visibility settings; changing this value moves the document to a different folder."},"contentType":{"type":"string","description":"MIME type of the document content, such as `application/pdf` or `image/png`."},"developerName":{"type":"string","description":"Unique API name used to reference the document in code, metadata, and integrations. Distinct from the display name; uses only letters, numbers, and underscores with no spaces."},"isInternalUseOnly":{"type":"boolean","description":"When true, marks the document for internal use only — it must not be shared with parties outside the organization."},"isPublic":{"type":"boolean","description":"When true, the document is accessible to all users; when false, access is limited to authorized users by Salesforce permissions and sharing settings."}},"description":"Salesforce Document record written by the import, combining the file content with metadata such as name, folder, and content type."},"attachment":{"type":"object","properties":{"id":{"type":"string","description":"Salesforce-assigned ID of the Attachment record. Prefer the 18-character case-insensitive form over the 15-character case-sensitive form in integrations."},"name":{"type":"string","description":"Filename of the attachment, including the file extension (for example, `contract_agreement.pdf`)."},"parentId":{"type":"string","description":"Salesforce record the attachment is linked to, such as an Account, Contact, or Opportunity. Required when creating an attachment; access to the attachment follows the parent record's permissions."},"contentType":{"type":"string","description":"MIME type of the attachment content, such as `application/pdf` or `image/png`."},"isPrivate":{"type":"boolean","description":"When true, the attachment is visible only to users explicitly authorized to access it; when false or omitted, it inherits the visibility of its parent record. This setting only restricts access further — it never grants access beyond the parent record's permissions."},"description":{"type":"string","description":"Free-text note describing the attachment's content or purpose, shown alongside the attachment details in Salesforce."}},"description":"Salesforce Attachment record written by the import — a file linked to a parent record via `parentId`, with metadata such as name and content type."},"contentVersion":{"type":"object","properties":{"contentDocumentId":{"type":"string","description":"ContentDocument this version belongs to; all versions of the same document share this ID. Assigned by Salesforce when the content version is created and immutable afterward."},"title":{"type":"string","description":"Display title of this content version, shown in content libraries, search results, and version history."},"pathOnClient":{"type":"string","description":"Original file path on the client machine before upload. Used for reference and display only; it does not affect how the file is stored or accessed in Salesforce."},"tagCsv":{"type":"string","description":"Comma-separated tags applied to the content version for categorization and search. Updating this field replaces the entire tag set, so include every tag the version should keep."},"contentLocation":{"type":"string","description":"Specifies where the content file for this ContentVersion record is stored — \"S\" for Salesforce internal storage or \"E\" for an external system (the two values the import form offers). Salesforce also defines \"L\" for content shared via a link, but the form does not set it. Typically assigned automatically based on the upload method."}},"description":"Salesforce ContentVersion record written by the import — a specific version of a content item in Salesforce Files. Each modification produces a new version, and all versions of a document are linked through `contentDocumentId`."}},"if":{"anyOf":[{"required":["attachment"]},{"required":["contentVersion"]}]},"then":{"required":["operation"]},"else":{"if":{"not":{"propertyNames":{"enum":["lookups","api"]}}},"then":{"required":["operation","api"],"if":{"properties":{"operation":{"const":"upsert"}},"required":["operation"]},"then":{"required":["idLookup","upsert"],"properties":{"idLookup":{"required":["extract"]},"upsert":{"required":["externalIdField"]}}},"else":{"if":{"properties":{"operation":{"enum":["update","delete","addupdate"]}},"required":["operation"]},"then":{"required":["idLookup"],"properties":{"idLookup":{"required":["whereClause"]}}},"else":{"if":{"properties":{"operation":{"const":"upsertpicklistvalues"}},"required":["operation"]},"then":{"required":["upsertpicklistvalues"],"properties":{"upsertpicklistvalues":{"required":["fullName"]}}}}}}}},"Tool":{"type":"object","description":"Configuration for a `ToolImport`, which executes a reusable Celigo Tool resource as the\nimport action. The referenced tool defines its own input contract, processing pipeline, and\noutput; this object binds the tool to the import and optionally overrides the connections the\ntool uses for this invocation.","required":["_toolId"],"properties":{"_toolId":{"type":"string","format":"objectId","description":"Tool resource this import executes. The tool supplies the input schema, routing, page\nprocessors, and output mapping; the import runs that tool when records reach it. Required —\na `ToolImport` has no behavior without a tool to run."},"overrides":{"type":"object","description":"Per-import overrides applied to the referenced tool at execution time. Tools use a\nbring-your-own-keys connection model: the tool declares abstract connection slots, and the\nconsumer binds concrete connections here so one tool definition can run against different\nsystems without being modified. Omit to run the tool with its default connections.","properties":{"connections":{"type":"array","description":"Connection bindings that replace the tool's abstract connection slots for this import.\nEach entry maps one of the tool's abstract connections to the concrete connection the\nimport should use.","items":{"type":"object","required":["_id","_abstractId"],"properties":{"_id":{"type":"string","format":"objectId","description":"Concrete connection bound to the abstract slot for this import's tool run."},"_abstractId":{"type":"string","format":"objectId","description":"Abstract connection slot, declared by the tool, that this binding replaces. Matches\nthe abstract connection id defined on the referenced tool."},"_borrowConcurrencyFromConnectionId":{"type":"string","format":"objectId","description":"Optional pointer to another connection whose\nconcurrency budget this binding shares. Use\nwhen several wrappers / steps should share rate\nlimits against the same downstream system but\nuse different credentials. Mutually exclusive\nwith `_id` on the same entry.\n"}}}}}}}},"File":{"type":"object","description":"**CRITICAL: This object is REQUIRED for all file-based import adaptor types.**\n\n**When to include this object**\n\n✅ **MUST SET** when `adaptorType` is one of:\n- `S3Import`\n- `FTPImport`\n- `AS2Import`\n- `HTTPImport` in file mode (`http.type: \"file\"` — the folder mode of the cloud file\n  providers such as Google Drive, Box, Dropbox, Azure Blob, Google Cloud Storage and Celigo\n  Storage); the same `file` object, including `type: filedefinition` with `_ediProfileId`\n  for EDI, applies as on S3 / FTP\n\n❌ **DO NOT SET** for non-file-based imports like:\n- `SalesforceImport`\n- `NetSuiteImport`\n- `HTTPImport` in records mode (`http.type` unset — a REST endpoint receiving records)\n- `MongodbImport`\n- `RDBMSImport`\n\n**Minimum required fields**\n\nFor most file imports, you need at minimum:\n- `fileName`: The output file name (supports Handlebars like `{{timestamp}}`)\n- `type`: The file format (json, csv, xml, xlsx)\n- `aggregation.type`: How the run's records are batched into files (`all` for one file per run)\n\n**Example (S3 Import)**\n\n```json\n{\n  \"file\": {\n    \"fileName\": \"customers-{{timestamp}}.json\",\n    \"aggregation\": { \"type\": \"all\" },\n    \"type\": \"json\"\n  },\n  \"s3\": {\n    \"region\": \"us-east-1\",\n    \"bucket\": \"my-bucket\",\n    \"fileKey\": \"customers-{{timestamp}}.json\"\n  },\n  \"adaptorType\": \"S3Import\"\n}\n```","properties":{"fileName":{"type":"string","description":"**REQUIRED for file-based imports.**\n\nThe name of the file to be created/written. Supports Handlebars expressions for dynamic naming.\n\n**Default behavior**\n- When the user does not specify a file naming convention, **always default to timestamped filenames** using `{{timestamp}}` (e.g., `\"items-{{timestamp}}.csv\"`). This ensures each run produces a unique file and avoids overwriting previous exports.\n- Only use a fixed filename (without timestamp) if the user explicitly requests overwriting or a fixed name.\n\n**Common patterns**\n- `\"data-{{timestamp}}.json\"` - Timestamped JSON file (DEFAULT — use this pattern when not specified)\n- `\"export-{{date}}.csv\"` - Date-stamped CSV file\n- `\"{{recordType}}-backup.xml\"` - Dynamic record type naming\n\n**Examples**\n- `\"customers-{{timestamp}}.json\"`\n- `\"orders-export.csv\"`\n- `\"inventory-{{date}}.xlsx\"`\n\n**Important**\n- For S3 imports, this should typically match the `s3.fileKey` value\n- For FTP imports, this should typically match the `ftp.fileName` value"},"skipAggregation":{"type":"boolean","description":"Legacy companion of `aggregation.type`: `true` when the run's records are written one\nfile per page, `false` when they are combined across the run or grouped by key.\nValidation reads the value sent — an EDI file definition needs `true` unless\n`aggregation.type` is `key`, and `batchSize` needs `true` — then the save rewrites it\nfrom `aggregation.type`, so send the two as a consistent pair.","default":false},"type":{"type":"string","enum":["json","csv","xml","xlsx","filedefinition"],"description":"**REQUIRED for file-based imports.**\n\nThe format of the output file.\n\nSelect the format that matches the target system's requirements."},"encoding":{"type":"string","enum":["utf8","win1252","utf-16le","gb18030","macroman","iso88591","shiftjis"],"description":"Character encoding for the output file.\n\n**Default:** `\"utf8\"`\n\nChange from the default only when the target system requires a specific encoding.","default":"utf8"},"delete":{"type":"boolean","description":"Whether to delete the source file after successful import.\n\n**Values**\n- `true`: Delete source file after processing\n- `false`: Keep source file\n\n**Default:** `false`","default":false},"compressionFormat":{"type":"string","enum":["gzip","zip"],"description":"Compression format for the output file.\n\n**When to use**\n- Large files that benefit from compression\n- When target system expects compressed files"},"backupPath":{"type":"string","description":"Path where backup copies of files should be stored.\n\n**Examples**\n- `\"backup/\"` - Relative backup folder\n- `\"/archive/2024/\"` - Absolute backup path"},"purgeInternalBackup":{"type":"boolean","description":"Whether to purge internal backup copies after successful processing.\n\n**Default:** `false`","default":false},"batchSize":{"type":"integer","description":"Number of records to include per batch/file when processing large datasets.\n\n**When to use**\n- Large imports that need to be split into multiple files\n- When target system has file size limitations\n\n**Note**\n- Offered when records are written one file per page (`aggregation.type: page` with\n  `skipAggregation: true`)"},"encrypt":{"type":"boolean","description":"Whether to encrypt the output file.\n\n**Values**\n- `true`: Encrypt the file (requires PGP configuration)\n- `false`: No encryption\n\n**Default:** `false`","default":false},"csv":{"type":"object","description":"CSV-specific configuration. Only used when `type` is `\"csv\"`.","properties":{"rowDelimiter":{"type":"string","description":"Character(s) used to separate rows. Default is newline.","default":"\n"},"columnDelimiter":{"type":"string","description":"Character(s) used to separate columns. Default is comma.","default":","},"includeHeader":{"type":"boolean","description":"When true, includes a header row with column names in the output.","default":true},"wrapWithQuotes":{"type":"boolean","description":"When true, wraps field values in quotes.","default":false},"replaceTabWithSpace":{"type":"boolean","description":"Replace tab characters with spaces.","default":false},"replaceNewlineWithSpace":{"type":"boolean","description":"Replace newline characters with spaces within fields.","default":false},"truncateLastRowDelimiter":{"type":"boolean","description":"Remove trailing row delimiter from the file.","default":false}}},"json":{"type":"object","description":"JSON-specific configuration. Only used when `type` is `\"json\"`.","properties":{"resourcePath":{"type":"string","description":"JSONPath expression to locate records within the JSON structure."}}},"xml":{"type":"object","description":"XML-specific configuration. Only used when `type` is `\"xml\"`.","properties":{"resourcePath":{"type":"string","description":"XPath expression to locate records within the XML structure."}}},"fileDefinition":{"type":"object","description":"Configuration settings for parsing files using a predefined file definition. This object enables processing of complex, non-standard, or proprietary file formats that require specialized parsing logic beyond what the standard parsers (CSV, JSON, XML, etc.) can handle.\n\n**When to use**\n\nConfigure this object when the `type` field is set to \"filedefinition\". This approach is required for properly handling:\n- Legacy or proprietary file formats with complex structures\n- Fixed-width text files where field positions are defined by character positions\n- Electronic Data Interchange (EDI) documents (X12, EDIFACT, etc.)\n- Multi-record type files where different lines have different formats\n- Files requiring complex preprocessing or custom parsing logic\n\n**File definition characteristics**\n\n- **Custom Parsing Rules**: Applies predefined parsing logic to complex file formats\n- **Reusable Configurations**: References externally defined parsing rules that can be reused\n- **Complex Format Support**: Handles formats that standard parsers cannot process\n- **Specialized Processing**: Often used for industry-specific or legacy formats\n\n**Implementation strategy for ai agents**\n\n1. **Format Analysis**:\n    - Determine if the file format is standard (CSV, JSON, XML) or requires custom parsing\n    - Check if the format follows industry standards like EDI, SWIFT, or fixed-width\n    - Assess if there are multiple record types within the same file\n    - Identify if specialized logic is needed to interpret the file structure\n\n2. **File Definition Selection**:\n    - Verify that a suitable file definition has already been created in the system\n    - Check if existing file definitions match the format requirements\n    - Confirm the file definition ID from system administrators if needed\n    - Ensure the file definition is compatible with the export's needs\n","properties":{"_fileDefinitionId":{"type":"string","format":"objectId","description":"Reference to the file definition resource."}}},"aggregation":{"type":"object","description":"Controls how a flow run's records are batched into output files — the transfer form's\n\"How many files would you like to generate?\" setting. The legacy `skipAggregation` flag\nmirrors `type` and is maintained by the platform.","properties":{"type":{"type":"string","enum":["all","page","key"],"description":"Which batches of a run's records become separate output files. EDI file definitions\n(`file.type: filedefinition`) are written per page or per key, never per run; `key`\nis accepted only for them and only when the account has B2B Manager. The grouping\nkey and the line-item loop it merges come from the EDI file definition's document\ntype, so there is no separate key field to configure."}}},"lookups":{"type":"array","description":"Named value-substitution maps available to this import's field mappings; each lookup\ntranslates source values to target values, with an optional default for unmatched values.\nReferenced by name from mapping rules.","items":{"type":"object","properties":{"name":{"type":"string","description":"Identifier used to reference this lookup from mapping rules."},"map":{"type":"object","description":"Source-value to target-value pairs applied during mapping.","additionalProperties":{"type":"string"}},"default":{"type":["string","null"],"description":"Value substituted when a source value is not found in map; empty string when omitted. May be stored as null (no default)."},"allowFailures":{"type":"boolean","description":"When true, a value missing from map does not fail the record and the default (or original value) is used."}}}},"pgp":{"type":"object","description":"PGP encryption settings applied when encrypt is true; selects the symmetric cipher and the\nsigning hash. The connection must hold the recipient's public key.","properties":{"symmetricKeyAlgorithm":{"type":"string","enum":["twofish","cast5","3des","aes128","aes192","aes256"],"default":"aes256","description":"Symmetric cipher used to encrypt the file body; aes256 unless the recipient requires another."},"hashAlgorithm":{"type":"string","enum":["sha256","sha384","sha512","sha224"],"description":"Hash algorithm used when signing the encrypted file; set only when the recipient requires a specific one."}}},"skipRename":{"type":"boolean","description":"When true, the import writes directly to the final file name instead of writing to a\ntemporary name and renaming on completion. Use only when the destination does not support\natomic rename; the default (false) uses the safe write-then-rename behavior."},"directory":{"type":"object","required":["pathMode"],"description":"Structured destination location for cloud file-provider imports (Google Drive shared\ndrives, Box, Dropbox), replacing the flat path used by classic FTP/S3 destinations. Select\nthe location by folder ID or by a path relative to a configured storage root via pathMode.\nFor these cloud providers, directoryId mode addresses the folder by id alone — the flat\ndestination-path field is not consulted at runtime.","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 destination directory (e.g. a Google Drive folder ID);\nset when pathMode is directoryId. This is the file provider's own ID, not a Celigo resource ID."},"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."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root the relative path resolves against; set when\npathMode is relativePath. The provider's own ID, not a Celigo resource ID."},"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 location for backup copies on cloud file providers — the file-provider\ncounterpart 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."}}}}},"FileSystem":{"type":"object","description":"Defines where files are written to 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 where generated files are written; the agent's OS\naccount must have write permission on it. Accepts a local OS path or a UNC network share,\nand supports handlebars templates for dynamic folders."}}},"AiAgentConfig":{"type":"object","description":"AI Agent configuration used by both AiAgentImport and GuardrailImport (ai_agent type).\n\nConfigures which AI provider and model to use, along with instructions, parameter\ntuning, output format, and available tools. Providers come in two families with\ntwo configuration formats:\n\n- **Built-in providers** — each has its own configuration block.\n  - **openai**: OpenAI models (GPT-4.1, GPT-5, etc.). Configure via the `openai` object.\n  - **gemini**: Google Gemini models via the LiteLLM proxy. Configure via `litellm` with overrides in `litellm._overrides.gemini`.\n  - **anthropic**: Anthropic Claude models via the LiteLLM proxy. Configure via `litellm` with overrides in `litellm._overrides.anthropic`.\n- **Catalog providers** — `mistral`, `xai`, `huggingface`, `deepseek`, `cohere`, `groq`. Added\n  as catalog data rather than platform code, they share one generic flat format:\n  `model`, `modelOptions`, `instructions`, `output`, and the `tools` / `prompts` /\n  `resources` arrays directly on this object.\n\nA `_connectionId` on the parent import selects bring-your-own-key (BYOK)\ncredentials. Without one, built-in providers run on platform-managed\ncredentials; catalog providers have no platform-managed credentials, so they\nsave without a connection but cannot run until one is attached.\n","required":["provider"],"properties":{"provider":{"type":"string","enum":["openai","gemini","anthropic","mistral","xai","huggingface","deepseek","cohere","groq"],"description":"AI provider to use."},"model":{"type":"string","description":"Model identifier for a catalog provider (generic flat format) — for example\n`grok-4.6`. Built-in providers set the model inside their own block\n(`openai.model`, `litellm.model`) instead."},"modelOptions":{"type":"object","description":"Model tuning for a catalog provider (generic flat format). Which keys, values, and\nranges are accepted is defined per catalog model and validated on save when the\nmodel is a catalog entry.","properties":{"maxOutputTokens":{"type":"number","description":"Maximum number of tokens the model may generate."},"temperature":{"type":"number","description":"Sampling temperature."},"topP":{"type":"number","description":"Nucleus sampling threshold."},"reasoning_effort":{"type":"string","enum":["low","medium","high","xhigh"],"description":"Reasoning depth for models that expose it."}}},"instructions":{"type":"string","description":"System prompt for a catalog provider (generic flat format). Built-in providers\nset it inside their own block (`openai.instructions`,\n`litellm._overrides.anthropic.systemInstruction`)."},"output":{"type":"object","description":"Output format for a catalog provider (generic flat format). Built-in providers\nconfigure it inside their own block (`openai.output`, `litellm.responseFormat`).","properties":{"format":{"type":"object","description":"Controls the structure of the model's output.","properties":{"type":{"type":"string","enum":["text","json_schema"],"description":"Output type."},"name":{"type":"string","description":"Name of the JSON schema, for `json_schema` output."},"strict":{"type":"boolean","description":"When true, the model must conform exactly to `jsonSchema`."},"jsonSchema":{"type":"object","additionalProperties":true,"description":"JSON Schema the structured output must conform to, for `json_schema` output."}}},"verbose":{"type":"string","description":"Level of detail in the model's response, for models that expose it."}}},"tools":{"type":"array","description":"Tools available to a catalog-provider agent (generic flat format). Entries mirror\nthe built-in providers' tool entries: a Celigo Tool by `_toolId`, an MCP server by\n`_mcpConnectionId`, or a vendor web-search tool.","items":{"type":"object","properties":{"type":{"type":"string","enum":["tool","mcp","web_search"],"description":"Type of tool entry."},"tool":{"type":"object","description":"Reference to a Celigo Tool resource, used when type is \"tool\".","properties":{"_toolId":{"type":"string","format":"objectId","description":"The Celigo Tool to call."}}},"mcp":{"type":"object","description":"MCP server connection, used when type is \"mcp\".","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if omitted). Each entry\nis either a plain tool name or an object carrying display metadata.","items":{"type":["string","object"]}}}},"config":{"type":"object","additionalProperties":true,"description":"Tool-specific options for vendor-native tools (for example web-search settings)."}}}},"prompts":{"type":"array","description":"MCP prompt entries available to a catalog-provider agent (generic flat format).\nEach entry references one MCP connection and the prompt names allowed from it.","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server that exposes the prompts."},"allowedPrompts":{"type":"array","description":"Prompt names the agent may fetch from the server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"},"openai":{"type":"object","description":"OpenAI-specific configuration. Used when `provider` is \"openai\".\n","required":["model","instructions"],"properties":{"instructions":{"type":"string","maxLength":1000000,"description":"System prompt that defines the AI agent's behavior, goals, and constraints.\n"},"model":{"type":"string","description":"OpenAI model identifier. Open string (not an enum) — model names change frequently."},"reasoning":{"type":"object","description":"Controls depth of reasoning for complex tasks.","properties":{"effort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh"],"description":"How much reasoning effort the model should invest"},"summary":{"type":"string","enum":["concise","auto","detailed"],"description":"Level of detail in reasoning summaries"}}},"temperature":{"type":"number","minimum":0,"maximum":2,"description":"Sampling temperature. Higher values (e.g. 1.5) produce more creative output,\nlower values (e.g. 0.2) produce more focused and deterministic output.\n"},"topP":{"type":"number","minimum":0.1,"maximum":1,"description":"Nucleus sampling parameter"},"topLogprobs":{"type":"number","minimum":0,"maximum":20,"description":"Number of most likely tokens to return log probabilities for at each output position."},"maxOutputTokens":{"type":"number","minimum":100,"maximum":128000,"default":5000,"description":"Maximum number of tokens in the model's response (server default observed live on create)"},"serviceTier":{"type":"string","enum":["auto","default","priority"],"default":"default","description":"OpenAI service tier. \"priority\" provides higher rate limits and\nlower latency at increased cost."},"output":{"type":"object","description":"Output format configuration","properties":{"format":{"type":"object","description":"Controls the structure of the model's output.\n","properties":{"type":{"type":"string","enum":["text","json_schema","blob"],"default":"text","description":"Output format type."},"schemaMode":{"type":"string","enum":["manual","json"],"description":"How the structured-output schema was authored in the UI.\nEditor state only — it does not change how `jsonSchema` is\nsent to the provider."},"name":{"type":"string","description":"Name for the output format (used with json_schema)"},"strict":{"type":"boolean","default":false,"description":"When true, enforces strict schema validation on output."},"jsonSchema":{"type":"object","description":"JSON Schema for structured output. Required when `format.type` is \"json_schema\".\n","properties":{"type":{"type":"string","description":"Root JSON Schema type of the structured output; use \"object\" for record-shaped results.","enum":["object","array","string","number","integer","boolean"]},"properties":{"type":"object","additionalProperties":true,"description":"JSON Schema definitions for each field the structured output may contain."},"required":{"type":"array","description":"Property names the model must include in the structured output.","items":{"type":"string"}},"additionalproperties":{"type":"boolean","description":"When true, the structured output may include properties beyond those defined in `properties`."}}}},"if":{"properties":{"type":{"const":"json_schema"}},"required":["type"]},"then":{"required":["name","jsonSchema"]}},"verbose":{"type":"string","enum":["low","medium","high"],"default":"medium","description":"Level of detail in the model's response"}}},"tools":{"type":"array","description":"Tools available to the AI agent during processing.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["web_search","mcp","image_generation","tool"],"description":"Type of tool."},"webSearch":{"type":"object","description":"Web search configuration (empty object to enable)"},"imageGeneration":{"type":"object","description":"Image generation configuration","properties":{"background":{"type":"string","description":"Controls whether generated images have a transparent or opaque background; use transparent only with output formats that support it (png, webp).","enum":["transparent","opaque"]},"quality":{"type":"string","description":"Rendering quality of generated images, trading detail for generation speed and file size.","enum":["low","medium","high"]},"size":{"type":"string","description":"Pixel dimensions of generated images; choose square, portrait, or landscape to match the intended use.","enum":["1024x1024","1024x1536","1536x1024"]},"outputFormat":{"type":"string","description":"File format of generated images; use png or webp when transparency is needed.","enum":["png","webp","jpeg"]}}},"mcp":{"type":"object","description":"MCP server tool configuration","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server"},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if\nomitted). Each entry is either a plain tool name (legacy\nform) or an object carrying display metadata.","items":{"type":["string","object"],"properties":{"name":{"type":"string","maxLength":256,"description":"Tool name as exposed by the MCP server."},"title":{"type":"string","maxLength":300,"description":"Display title shown for the tool."},"description":{"type":"string","maxLength":1000,"description":"Display description shown for the tool."}},"required":["name"]}},"allowedPrompts":{"type":"array","description":"Specific prompts to allow from the MCP server (used for MCP prompt entries; all if omitted).","items":{"type":"string"}}}},"tool":{"type":"object","description":"Reference to a Celigo Tool resource.\n","properties":{"_toolId":{"type":"string","format":"objectId","description":"Reference to the Tool resource"},"overrides":{"type":"object","description":"Per-agent overrides for the tool's internal resources","properties":{"connections":{"type":"array","description":"Remaps the tool's abstract connections for this agent. Each entry pairs\nthe tool's abstract connection placeholder (`_abstractId`) with the\nconcrete connection (`_id`) to use for this agent; entries without\n`_id` keep the tool's own default connection.\n","items":{"type":["object","null"],"required":["_abstractId"],"properties":{"_abstractId":{"type":"string","format":"objectId","description":"The tool's abstract connection placeholder being overridden."},"_id":{"type":"string","format":"objectId","description":"Concrete connection to use in place of the abstract placeholder."}}}}}}}}}}},"prompts":{"type":"array","description":"MCP prompt entries available to the agent. Each item references one MCP connection\nand the prompt names allowed from it. Configured alongside `tools` in the form but\nstored separately; an entry's `allowedPrompts` is what distinguishes a prompt entry\nfrom an MCP tool entry (which carries `allowedTools`).\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","description":"MCP server prompt configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedPrompts":{"type":"array","description":"Prompt names to allow from the MCP server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"}}},"litellm":{"type":"object","description":"LiteLLM proxy configuration. Used when `provider` is \"gemini\" or \"anthropic\".\n\nLiteLLM provides a unified interface to multiple AI providers. Gemini-specific\nsettings are in `_overrides.gemini`; Claude-specific settings are in\n`_overrides.anthropic`.\n\n`model` is required when litellm is the active provider path.\n","properties":{"model":{"type":"string","description":"LiteLLM model identifier. For Gemini, models are stored without the `gemini/`\nprefix; for Anthropic, use the Claude model id (e.g. `claude-sonnet-4-6`)."},"temperature":{"type":"number","minimum":0,"maximum":2,"description":"Sampling temperature"},"maxCompletionTokens":{"type":"number","minimum":100,"maximum":128000,"default":5000,"description":"Maximum number of tokens in the response"},"topP":{"type":"number","minimum":0.1,"maximum":1,"description":"Nucleus sampling parameter"},"seed":{"type":"number","description":"Random seed for reproducible outputs"},"responseFormat":{"type":"object","description":"Output format configuration","properties":{"type":{"type":"string","description":"Output format type.","enum":["text","json_schema","blob"],"default":"text"},"schemaMode":{"type":"string","enum":["manual","json"],"description":"How the structured-output schema was authored in the UI. Editor\nstate only — it does not change how `jsonSchema` is sent to the\nprovider."},"name":{"type":"string","description":"Name for the output format (used with json_schema)."},"strict":{"type":"boolean","description":"When true, enforces strict schema validation on output.","default":false},"jsonSchema":{"type":"object","description":"JSON Schema for structured output. Required when `responseFormat.type` is \"json_schema\".","properties":{"type":{"type":"string","description":"Root JSON Schema type of the structured output; use \"object\" for record-shaped results.","enum":["object","array","string","number","integer","boolean"]},"properties":{"type":"object","additionalProperties":true,"description":"JSON Schema definitions for each field the structured output may contain."},"required":{"type":"array","description":"Property names the model must include in the structured output.","items":{"type":"string"}},"additionalProperties":{"type":"boolean","description":"When true, the structured output may include properties beyond those defined in `properties`."}}}},"if":{"properties":{"type":{"const":"json_schema"}},"required":["type"]},"then":{"required":["name","jsonSchema"]}},"_overrides":{"type":"object","description":"Provider-specific overrides","properties":{"gemini":{"type":"object","description":"Gemini-specific configuration overrides.\n","required":["systemInstruction"],"properties":{"systemInstruction":{"type":"string","maxLength":1000000,"description":"System instruction for Gemini models. Equivalent to OpenAI's `instructions`.\nMaximum 1,000,000 characters.\n"},"tools":{"type":"array","description":"Gemini-specific tools","items":{"type":"object","properties":{"type":{"type":"string","enum":["googleSearch","urlContext","fileSearch","mcp","tool"],"description":"Type of Gemini tool."},"googleSearch":{"type":"object","description":"Google Search configuration (empty object to enable)"},"urlContext":{"type":"object","description":"URL context configuration (empty object to enable)"},"fileSearch":{"type":"object","description":"File search configuration, used when type is \"fileSearch\".","properties":{"fileSearchStoreNames":{"type":"array","description":"Names of the file search stores the model can query.","items":{"type":"string"}}}},"mcp":{"type":"object","description":"MCP server tool configuration, used when type is \"mcp\".","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if\nomitted). Each entry is either a plain tool name\n(legacy form) or an object carrying display metadata\n— same contract as the OpenAI `allowedTools`.","items":{"type":["string","object"],"properties":{"name":{"type":"string","maxLength":256,"description":"Tool name as exposed by the MCP server."},"title":{"type":"string","maxLength":300,"description":"Display title shown for the tool."},"description":{"type":"string","maxLength":1000,"description":"Display description shown for the tool."}},"required":["name"]}},"allowedPrompts":{"type":"array","description":"Specific prompts to allow from the MCP server (used for MCP prompt entries; all if omitted).","items":{"type":"string"}}}},"tool":{"type":"object","description":"Reference to a Celigo Tool resource, used when type is \"tool\".","properties":{"_toolId":{"type":"string","format":"objectId","description":"Reference to the Tool resource."},"overrides":{"type":"object","description":"Per-agent overrides for the tool's internal resources.","properties":{"connections":{"type":"array","description":"Remaps the tool's abstract connections for this agent. Each entry\npairs the tool's abstract connection placeholder (`_abstractId`)\nwith the concrete connection (`_id`) to use for this agent;\nentries without `_id` keep the tool's own default connection.\n","items":{"type":["object","null"],"required":["_abstractId"],"properties":{"_abstractId":{"type":"string","format":"objectId","description":"The tool's abstract connection placeholder being overridden."},"_id":{"type":"string","format":"objectId","description":"Concrete connection to use in place of the abstract placeholder."}}}}}}}}}}},"prompts":{"type":"array","description":"MCP prompt entries available to the Gemini agent. Each item references one\nMCP connection and the prompt names allowed from it. The presence of\n`allowedPrompts` distinguishes a prompt entry from an MCP tool entry.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","description":"MCP server prompt configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedPrompts":{"type":"array","description":"Prompt names to allow from the MCP server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"},"responseModalities":{"type":"array","description":"Response output modalities","items":{"type":"string","enum":["text","image"]},"default":["text"]},"topK":{"type":"number","description":"Top-K sampling parameter for Gemini"},"thinkingConfig":{"type":"object","description":"Controls Gemini's extended thinking capabilities","properties":{"includeThoughts":{"type":"boolean","description":"When true, includes the model's thinking steps in the response."},"thinkingBudget":{"type":"number","minimum":100,"maximum":4000,"description":"Maximum tokens allocated for thinking"},"thinkingLevel":{"type":"string","description":"Controls how much thinking effort the model applies; use higher levels for complex, multi-step tasks at the cost of latency and tokens.","enum":["minimal","low","medium","high"]}}},"imageConfig":{"type":"object","description":"Gemini image generation configuration","properties":{"aspectRatio":{"type":"string","description":"Aspect ratio of generated images; choose a ratio matching the intended display format.","enum":["1:1","2:3","3:2","3:4","4:3","4:5","5:4","9:16","16:9","21:9"]},"imageSize":{"type":"string","description":"Output resolution of generated images; higher resolutions increase detail and file size.","enum":["1K","2K","4K"]}}},"mediaResolution":{"type":"string","enum":["low","medium","high"],"description":"Resolution for media inputs (images, video)"}}},"anthropic":{"type":"object","description":"Claude-specific configuration overrides. Used when `provider` is \"anthropic\".\n","required":["systemInstruction"],"properties":{"systemInstruction":{"type":"string","maxLength":1000000,"description":"System instruction for Claude models. Equivalent to OpenAI's `instructions`.\nMaximum 1,000,000 characters.\n"},"topK":{"type":"number","minimum":0,"description":"Top-K sampling parameter. Deprecated on Claude models released after Claude Opus\n4.6, which reject any value at runtime; set it only on older models."},"thinkingConfig":{"type":"object","description":"Controls Claude's extended thinking capabilities.","properties":{"type":{"type":"string","enum":["enabled","disabled","adaptive"],"default":"disabled","description":"Extended thinking mode."},"budgetTokens":{"type":"number","minimum":1024,"description":"Maximum tokens allocated for thinking. Required when `type` is \"enabled\" —\nomitting it fails the save with 422 `invalid_thinking_config`."},"display":{"type":"string","enum":["summarized","omitted"],"default":"summarized","description":"How thinking output is surfaced in the response."},"effort":{"type":"string","enum":["low","medium","high","xhigh","max"],"description":"How much thinking effort the model applies; use with `type` \"adaptive\". Higher\nvalues (`xhigh`, `max`) may be gated to specific Claude models by the provider."}},"if":{"properties":{"type":{"const":"enabled"}},"required":["type"]},"then":{"required":["budgetTokens"]}},"serviceTier":{"type":"string","enum":["auto","standard_only"],"default":"auto","description":"Anthropic service tier for the request."},"tools":{"type":"array","description":"Claude-specific tools.","items":{"type":"object","properties":{"type":{"type":"string","enum":["tool","mcp","webSearch"],"description":"Type of Claude tool."},"tool":{"type":"object","description":"Reference to a Celigo Tool resource, used when type is \"tool\".","properties":{"_toolId":{"type":"string","format":"objectId","description":"Reference to the Tool resource."},"overrides":{"type":"object","description":"Per-agent overrides for the tool's internal resources.","properties":{"connections":{"type":"array","description":"Remaps the tool's abstract connections for this agent. Each entry\npairs the tool's abstract connection placeholder (`_abstractId`)\nwith the concrete connection (`_id`) to use for this agent;\nentries without `_id` keep the tool's own default connection.\n","items":{"type":["object","null"],"required":["_abstractId"],"properties":{"_abstractId":{"type":"string","format":"objectId","description":"The tool's abstract connection placeholder being overridden."},"_id":{"type":"string","format":"objectId","description":"Concrete connection to use in place of the abstract placeholder."}}}}}}}},"mcp":{"type":"object","description":"MCP server tool configuration, used when type is \"mcp\".","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if omitted).","items":{"type":"string"}}}},"webSearch":{"type":"object","description":"Web search configuration, used when type is \"webSearch\".","properties":{"version":{"type":"string","pattern":"^\\d{8}$","description":"Anthropic web search tool version (YYYYMMDD). Selects the tool version sent\non the wire; unsupported versions surface as an Anthropic 400."},"allowedDomains":{"type":"array","description":"Domains the search may return results from. Mutually exclusive with `blockedDomains`.","items":{"type":"string"}},"blockedDomains":{"type":"array","description":"Domains to exclude from search results. Mutually exclusive with `allowedDomains`.","items":{"type":"string"}},"userLocation":{"type":"object","description":"Approximate user location used to localize search results. When present, at\nleast one of `city`, `country`, `region`, or `timezone` must be set.","properties":{"type":{"type":"string","enum":["approximate"],"description":"Location type. Always \"approximate\"."},"city":{"type":"string","maxLength":256,"description":"City name for localizing search results."},"country":{"type":"string","maxLength":8,"description":"ISO 3166-1 alpha-2 country code for localizing search results."},"region":{"type":"string","maxLength":256,"description":"Region or state for localizing search results."},"timezone":{"type":"string","maxLength":64,"description":"IANA timezone for localizing search results."}}}}}}}},"prompts":{"type":"array","description":"MCP prompt entries available to the Claude agent. Each item references one MCP\nconnection and the prompt names allowed from it. The presence of `allowedPrompts`\ndistinguishes a prompt entry from an MCP tool entry.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","description":"MCP server prompt configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedPrompts":{"type":"array","description":"Prompt names to allow from the MCP server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"}}}}}}}},"if":{"properties":{"provider":{"const":"openai"}},"required":["provider"]},"then":{"required":["openai"],"properties":{"openai":{"required":["model","instructions"]}}},"else":{"if":{"properties":{"provider":{"enum":["gemini","anthropic"]}},"required":["provider"]},"then":{"required":["litellm"],"properties":{"litellm":{"required":["model"]}}},"else":{"if":{"properties":{"provider":{"enum":["mistral","xai","huggingface","deepseek","cohere","groq"]}},"required":["provider"]},"then":{"required":["model"]}}}},"McpResources":{"type":"array","description":"Governed MCP resources — read-only reference content (policies, schemas, documentation)\npulled from connected MCP servers and made available to the agent as a consistent source\nof truth. Each entry references one MCP connection and the specific resources allowed from it.","items":{"type":"object","required":["type","mcp"],"properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of resource entry. Always \"mcp\"."},"mcp":{"type":"object","required":["_mcpConnectionId","allowedResources"],"description":"MCP server resource configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server that exposes the resources."},"allowedResources":{"type":"array","minItems":1,"description":"Resources to expose to the agent from the MCP server. Must contain at least one\nentry; each entry identifies one resource by name and URI.","items":{"type":"object","required":["name","uri"],"properties":{"name":{"type":"string","description":"Display name of the MCP resource."},"uri":{"type":"string","description":"URI that identifies the resource on the MCP server."}}}}}}}}},"GuardrailConfig":{"type":"object","description":"Configuration for GuardrailImport adaptor type.\n\nGuardrails evaluate data flowing through integrations for safety and\ncompliance. The `type` field selects which check to apply, and the\ncorresponding sub-object (`aiAgent`, `pii`, or `moderation`) provides\nthe configuration.\n\nA `_connectionId` on the parent import is only needed for BYOK\n`ai_agent` guardrails. In responses the server echoes the active type's\nsub-object and applies the `confidenceThreshold` default; it also\nreturns inactive sibling sub-objects (e.g. `moderation: {categories: []}`\non a `pii` guardrail, or a populated `pii` left over from a type switch),\nbut only the active type's sub-object is meaningful. Legacy documents\nmay carry a server-written default `aiAgent` stub on `pii`/`moderation`\nguardrails; current servers strip the inactive `aiAgent` on write.","properties":{"type":{"type":"string","enum":["ai_agent","pii","moderation"],"description":"The type of guardrail to apply. Each type requires its corresponding\nsub-configuration object (`aiAgent`, `pii`, or `moderation`)."},"confidenceThreshold":{"type":"number","minimum":0,"maximum":1,"default":0.7,"description":"Confidence threshold (0 to 1). Detections below this threshold are\nignored. Lower values catch more issues but increase false positives."},"aiAgent":{"type":"object","description":"AI agent check configuration; set when `type` is `ai_agent`. On\n`pii`/`moderation` guardrails a legacy server-written stub may\nappear here — it is inert, and current servers strip it on write."},"pii":{"type":"object","required":["entities"],"description":"PII detection configuration. Required when `type` is `pii`.","properties":{"entities":{"type":"array","description":"PII entity types to detect. When `type` is `pii`, at least one\nentry is required; the inactive sibling on other guardrail types\nmay be served with an empty list.","items":{"type":"string","enum":["credit_card_number","card_security_code_cvv_cvc","cryptocurrency_wallet_address","date_and_time","email_address","iban_code","bic_swift_bank_identifier_code","ip_address","location","medical_license_number","national_registration_number","persons_name","phone_number","url","us_bank_account_number","us_drivers_license","us_itin","us_passport_number","us_social_security_number","uk_nhs_number","uk_national_insurance_number","spanish_nif","spanish_nie","italian_fiscal_code","italian_drivers_license","italian_vat_code","italian_passport","italian_identity_card","polish_pesel","finnish_personal_identity_code","singapore_nric_fin","singapore_uen","australian_abn","australian_acn","australian_tfn","australian_medicare","indian_pan","indian_aadhaar","indian_vehicle_registration","indian_voter_id","indian_passport","korean_resident_registration_number"]}},"mask":{"type":"boolean","default":false,"description":"When true, detected PII is replaced with masked values.\nWhen false, PII is flagged without modification."}}},"moderation":{"type":"object","required":["categories"],"description":"Content moderation configuration. Required when `type` is `moderation`.","properties":{"categories":{"type":"array","description":"Content moderation categories to check. When `type` is\n`moderation`, at least one entry is required; the inactive\nsibling on other guardrail types may be served with an empty\nlist.","items":{"type":"string","enum":["sexual","sexual_minors","hate","hate_threatening","harassment","harassment_threatening","self_harm","self_harm_intent","self_harm_instructions","violence","violence_graphic","illicit","illicit_violent"]}}}}},"required":["type","confidenceThreshold"],"if":{"required":["type"],"properties":{"type":{"const":"pii"}}},"then":{"required":["pii"],"properties":{"pii":{"required":["entities"],"properties":{"entities":{"minItems":1}}}}},"else":{"if":{"required":["type"],"properties":{"type":{"const":"moderation"}}},"then":{"required":["moderation"],"properties":{"moderation":{"required":["categories"],"properties":{"categories":{"minItems":1}}}}},"else":{"required":["aiAgent"],"properties":{"aiAgent":{"$ref":"#/components/schemas/AiAgentConfig"}}}}},"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"},"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\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**: Using wildcards like `*` and `?`\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- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"isempty\"`: Field is empty or null (is empty)\n- `\"isnotempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches specified pattern (matches)\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- Returns the raw field value which may need type conversion\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  - `export.settingName`: Access export-level settings\n  - `import.settingName`: Access import-level settings\n  - `integration.settingName`: Access integration-level settings\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\nType conversion operators are often necessary when comparing extracted field values against literals or when the field type doesn't match the comparison operator's expected type. 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    \"extract\",\n    \"status\"\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"extract\",\n    \"filename\"\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\n```json\n[\n  \"greaterthan\",\n  [\n    \"extract\",\n    \"orderDate\"\n  ],\n  \"2023-01-01T00:00:00Z\"\n]\n```\n\nExample 5: Category contains any of [\"Urgent\", \"High Priority\"]\n```json\n[\n  \"anyof\",\n  [\n    \"extract\",\n    \"category\"\n  ],\n  [\"Urgent\", \"High Priority\"]\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 that exists in the system.\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"}}}}},"Hook":{"type":"object","description":"A single lifecycle hook — a JavaScript function invoked from a script (or hosted on a\nstack) at a fixed point in an import or AI-agent run. Shared by every hook slot; the\nslot's own description says when in the lifecycle it fires.","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","null"],"description":"Static parameters passed to the hook function at runtime, letting one script be\nreused with different settings. Null when the hook carries no static parameters."}}},"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.\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"}}}}},"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, arrays of objects, arrays of arrays of objects) are ALL\n   handled through this recursive pattern\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","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- For arrays within arrays: Leave EMPTY for the inner array mappings, as they don't have field names\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, empty 'generate' fields almost always indicate inner array structures\nwithin a parent array.\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, attempts conversion from strings\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. REQUIRES child mappings in the 'mappings' array\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- `arrayarray`: Array of arrays (for matrix/table structures)\n\nArray dataTypes can be populated two ways: pass a source array through\nunchanged via `extract` alone (when the source is already an array of\nthe right shape), or construct/iterate via `buildArrayHelper`.\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` is one option (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","arrayarray"]},"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\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 `object`, `objectarray`, and primitive-array dataTypes as\n  well as scalars — the rendered template output must parse into the\n  declared shape\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- When both extract and mappings are defined for objects, extract is applied first\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 empty (the UI's \"Do nothing\" action).\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.\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"},"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, date)\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 REQUIRED mechanism for ALL array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.)\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**For Arrays of Arrays (arrayarray)**\n- Similar to objectarray, but inner arrays have empty 'generate' fields\n- Used for matrix/table structures\n```json\n{\n  \"generate\": \"matrix\",\n  \"dataType\": \"arrayarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.rows[*]\",  // For each row in the rows array\n      \"mappings\": [\n        {\n          \"dataType\": \"numberarray\",  // Note: No generate field for inner arrays\n          \"buildArrayHelper\": [\n            {\"extract\": \"$.rows.columns[*]\"}  // Notice: \"rows\" is singular in the composite object\n          ]\n        }\n      ]\n    }\n  ]\n}\n```\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"},"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":"Specifies the condition that must be met for an array element to be included.\n\n'extract_not_empty' only includes elements where the extract field returns a value.\n","enum":["extract_not_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"}}}}},"status":{"type":"string","description":"**Purpose**\nRequired on every mapping entry. Controls whether the mapping is applied.\n\n**Guidance**\nEmit `\"Active\"` for mappings that should run; `\"Draft\"` saves an\nin-progress mapping without the Active-only field validations. The\nAPI rejects a mapping missing this field\n(validation error: \"Mapping object must have status field present.\").\n","enum":["Active","Draft"]},"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**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":"Specifies the condition that triggers application of this mapping:\n- record_created: Apply only when creating a new record\n- record_updated: Apply only when updating an existing record\n- extract_not_empty: Apply only when the extract field returns a value\n","enum":["record_created","record_updated","extract_not_empty"]}}}},"required":["dataType"]}},"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- For arrays within arrays: Leave EMPTY for the inner array mappings, as they don't have field names\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, empty 'generate' fields almost always indicate inner array structures\nwithin a parent array.\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, attempts conversion from strings\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. REQUIRES child mappings in the 'mappings' array\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- `arrayarray`: Array of arrays (for matrix/table structures)\n\nArray dataTypes can be populated two ways: pass a source array through\nunchanged via `extract` alone (when the source is already an array of\nthe right shape), or construct/iterate via `buildArrayHelper`.\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` is one option (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","arrayarray"]},"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\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 `object`, `objectarray`, and primitive-array dataTypes as\n  well as scalars — the rendered template output must parse into the\n  declared shape\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- When both extract and mappings are defined for objects, extract is applied first\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 empty (the UI's \"Do nothing\" action).\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.\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"},"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, date)\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 REQUIRED mechanism for ALL array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.)\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**For Arrays of Arrays (arrayarray)**\n- Similar to objectarray, but inner arrays have empty 'generate' fields\n- Used for matrix/table structures\n```json\n{\n  \"generate\": \"matrix\",\n  \"dataType\": \"arrayarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.rows[*]\",  // For each row in the rows array\n      \"mappings\": [\n        {\n          \"dataType\": \"numberarray\",  // Note: No generate field for inner arrays\n          \"buildArrayHelper\": [\n            {\"extract\": \"$.rows.columns[*]\"}  // Notice: \"rows\" is singular in the composite object\n          ]\n        }\n      ]\n    }\n  ]\n}\n```\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"},"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":"Specifies the condition that must be met for an array element to be included.\n\n'extract_not_empty' only includes elements where the extract field returns a value.\n","enum":["extract_not_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"}}}}},"status":{"type":"string","description":"**Purpose**\nRequired on every mapping entry. Controls whether the mapping is applied.\n\n**Guidance**\nEmit `\"Active\"` for mappings that should run; `\"Draft\"` saves an\nin-progress mapping without the Active-only field validations. The\nAPI rejects a mapping missing this field\n(validation error: \"Mapping object must have status field present.\").\n","enum":["Active","Draft"]},"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**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":"Specifies the condition that triggers application of this mapping:\n- record_created: Apply only when creating a new record\n- record_updated: Apply only when updating an existing record\n- extract_not_empty: Apply only when the extract field returns a value\n","enum":["record_created","record_updated","extract_not_empty"]}}}},"required":["dataType"]},"MappingField":{"type":"object","description":"One Mapper 1.0 field-mapping entry.","properties":{"generate":{"type":"string","description":"Target field path to write on the destination record."},"extract":{"type":"string","description":"Source expression. Accepts:\n- JSONPath starting with `$.` (e.g. `$.customer.firstName`).\n- Handlebars template (contains `{{`) for concatenation /\n  formatting / conditional logic (e.g.\n  `{{record.firstName}} {{record.lastName}}`).\n- A literal string (any value not starting with `$.` and not\n  containing `{{`) — treated as a hardcoded value.\n\nOmit when using `hardCodedValue`.\n"},"hardCodedValue":{"type":["string","null"],"description":"Static value written to `generate` instead of extracting from the source record."},"dataType":{"type":"string","enum":["string","number","boolean","numberarray","stringarray","json"],"description":"Data type coercion applied to the mapped value."},"discardIfEmpty":{"type":"boolean","description":"When true, the field is omitted from the output when the extracted value is empty."},"immutable":{"type":"boolean","description":"When true, the mapped value cannot be overwritten by later mapping steps."},"lookupName":{"type":"string","description":"Name of an entry in the import's `lookups` array used to translate the value."},"default":{"type":["string","null"],"description":"Fallback value used when the extract yields no value."},"extractDateFormat":{"type":"string","description":"Date format of the source value, used to parse it before conversion."},"extractDateTimezone":{"type":"string","description":"Timezone applied when parsing the source date value."},"generateDateFormat":{"type":"string","description":"Date format applied to the value written to the destination."},"generateDateTimezone":{"type":"string","description":"Timezone applied when formatting the destination date value."},"conditional":{"type":"object","description":"Only apply this mapping entry when the given condition is satisfied.\n","properties":{"when":{"type":"string","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","expression"],"description":"Condition that gates whether this mapping entry is applied.\n`lookup_not_empty` / `lookup_empty` evaluate the lookup named by the\nsibling `lookupName`; `expression` evaluates the sibling `expression`.\n"},"lookupName":{"type":"string","description":"Lookup to evaluate for the `lookup_not_empty` / `lookup_empty`\nconditions.\n"},"expression":{"type":"string","description":"Expression evaluated when `when` is `expression`.\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.\nThis value typically matches the key in the fieldMap object.\n"},"name":{"type":"string","description":"Name of the field, used as the property name when generating the settings object\nfrom the submitted form data.\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.\n","default":false},"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 fields, specifies the placeholder and field name for the key input.\n"},"valueName":{"type":"string","description":"For keyvalue fields, specifies the placeholder and field name for the value input.\n"},"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.\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"]},"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.\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","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. This script must be accessible within the user's account\nand have appropriate permissions.\n"}}}}},"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"}}},"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},"Import":{"type":"object","required":["_id","name","adaptorType","apiIdentifier","createdAt","lastModified"],"description":"Import object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ImportBase"},{"$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 import."},"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the source resource this import was created from."},"_templateId":{"type":"string","format":"objectId","readOnly":true,"description":"Template this import was created from."},"draft":{"type":"boolean","readOnly":true,"description":"When true, this import 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 import expires."},"debugUntil":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp until which debug logging is enabled for this import."}}}]},"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":"Reference to the integration app that defines this resource.\n\nThis field is only populated for resources that are part of an integration app.\nIt contains the unique identifier (_id) of the integration app (connector)\nthat defines the structure, behavior, and templates for this resource.\n\nThe integration app is the published template that can be installed\nmultiple times across different accounts, with each installation creating\na separate integration instance (referenced by _integrationId).\n\nThis reference enables:\n- Identifying the source integration app for this resource\n- Determining which template version is being used\n- Linking to documentation, support, and marketplace information\n"}}},"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/imports/{_id}":{"put":{"summary":"Update an import","description":"Updates an existing import with the provided configuration.\nThis is used for major updates to an import's structure or behavior.\n","operationId":"updateImport","tags":["Imports"],"parameters":[{"name":"_id","in":"path","description":"The unique identifier of the import","required":true,"schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Request"}}}},"responses":{"200":{"description":"Import updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Import"}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
````

## Delete an import

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

```json
{"openapi":"3.2.0","info":{"title":"Imports","version":"1.0.0"},"tags":[{"name":"Imports","description":"Imports deliver data to destination systems. An import receives pages of records from\nupstream flow steps, applies field mappings and transformations, and writes the results\nto the target application, database, or file destination — handling lookups, duplicate\navoidance, and per-record error reporting along the way. Each import uses one\n`adaptorType` that determines its connection compatibility and configuration object.\n\n## Import schema\n\n{% openapi-schemas spec=\"import\" schemas=\"Import\" 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/imports/{_id}":{"delete":{"summary":"Delete an import","description":"Deletes an import. The import is soft-deleted and retained in the recycle bin\nfor 30 days before permanent removal. If the import is currently in use by\nany flows, those flows may fail until reconfigured.\n","operationId":"deleteImport","tags":["Imports"],"parameters":[{"name":"_id","in":"path","description":"The unique identifier of the import","required":true,"schema":{"type":"string","format":"objectId"}}],"responses":{"204":{"description":"Import deleted successfully"},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"},"422":{"$ref":"#/components/responses/422-dependency-conflict"}}}}}}
```

## Patch an import

> Partially updates an import using a JSON Patch document (RFC 6902).\
> Only the \`replace\` operation is supported, and only on the following\
> whitelisted path:\
> \
> \| Path | Description |\
> \|------|-------------|\
> \| \`/debugUntil\` | Debug logging expiry (ISO-8601, max 1 hour from now) |\
> \
> All other paths are rejected with \`422\`.

```json
{"openapi":"3.2.0","info":{"title":"Imports","version":"1.0.0"},"tags":[{"name":"Imports","description":"Imports deliver data to destination systems. An import receives pages of records from\nupstream flow steps, applies field mappings and transformations, and writes the results\nto the target application, database, or file destination — handling lookups, duplicate\navoidance, and per-record error reporting along the way. Each import uses one\n`adaptorType` that determines its connection compatibility and configuration object.\n\n## Import schema\n\n{% openapi-schemas spec=\"import\" schemas=\"Import\" 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/imports/{_id}":{"patch":{"summary":"Patch an import","description":"Partially updates an import using a JSON Patch document (RFC 6902).\nOnly the `replace` operation is supported, and only on the following\nwhitelisted path:\n\n| Path | Description |\n|------|-------------|\n| `/debugUntil` | Debug logging expiry (ISO-8601, max 1 hour from now) |\n\nAll other paths are rejected with `422`.","operationId":"patchImport","tags":["Imports"],"parameters":[{"name":"_id","in":"path","description":"The unique identifier of the import","required":true,"schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonPatchRequest"}}}},"responses":{"204":{"description":"Import patched successfully"},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
```

## Clone an import

> Creates a copy of an existing import.\
> Supports optionally remapping referenced connections (via connectionMap).<br>

````json
{"openapi":"3.2.0","info":{"title":"Imports","version":"1.0.0"},"tags":[{"name":"Imports","description":"Imports deliver data to destination systems. An import receives pages of records from\nupstream flow steps, applies field mappings and transformations, and writes the results\nto the target application, database, or file destination — handling lookups, duplicate\navoidance, and per-record error reporting along the way. Each import uses one\n`adaptorType` that determines its connection compatibility and configuration object.\n\n## Import schema\n\n{% openapi-schemas spec=\"import\" schemas=\"Import\" 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 import.","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":"Optional mapping of original connection ids to replacement connection ids.\nKeys are source connection ids on the original resource; values are target connection ids.\n","additionalProperties":{"type":"string"}}},"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/Import"},{"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."}}}}]},"Import":{"type":"object","required":["_id","name","adaptorType","apiIdentifier","createdAt","lastModified"],"description":"Import object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ImportBase"},{"$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 import."},"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the source resource this import was created from."},"_templateId":{"type":"string","format":"objectId","readOnly":true,"description":"Template this import was created from."},"draft":{"type":"boolean","readOnly":true,"description":"When true, this import 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 import expires."},"debugUntil":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp until which debug logging is enabled for this import."}}}]},"ImportBase":{"type":"object","description":"Writable import fields shared by the request and response schemas.","properties":{"_connectionId":{"type":"string","format":"objectId","description":"Connection this import uses to reach the destination system. The connection's type must\nbe compatible with the import's `adaptorType` (e.g. an `HTTPImport` needs an `http`\nconnection; `NetSuiteDistributedImport` and `NetSuiteHTTPImport` both need a `netsuite`\nconnection). Server-required — POST without it fails with 422 \"Expected field:\n_connectionId to be present\" — except for the connection-less flavors (`ToolImport`,\n`AiAgentImport`, `GuardrailImport`), which the server creates without one."},"_integrationId":{"type":["string","null"],"format":"objectId","description":"Integration this import belongs to."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this import was created from, set when the import is part of an installed integration app."},"adaptorType":{"type":"string","description":"Selects the adaptor technology that executes this import, which determines the compatible\nconnection types and which adaptor-specific configuration object must also be supplied\n(e.g. set `salesforce` when using `SalesforceImport`).","enum":["HTTPImport","FTPImport","AS2Import","S3Import","NetSuiteImport","NetSuiteDistributedImport","NetSuiteHTTPImport","SalesforceImport","JDBCImport","RDBMSImport","MongodbImport","DynamodbImport","WrapperImport","AiAgentImport","GuardrailImport","FileSystemImport","ToolImport","RESTImport"]},"nsDomainType":{"type":"string","enum":["suitetalk","restlet"],"description":"Selects which NetSuite REST host a `NetSuiteHTTPImport` 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 each\n`http.relativeURI` entry carries the complete path starting at `/services/rest/...` or\n`/app/site/hosting/restlet.nl`. Ignored on other adaptor types."},"externalId":{"type":["string","null"],"description":"External identifier for correlating the import with a record in another system."},"as2":{"$ref":"#/components/schemas/As2"},"dynamodb":{"$ref":"#/components/schemas/Dynamodb"},"http":{"$ref":"#/components/schemas/Http"},"ftp":{"$ref":"#/components/schemas/Ftp"},"jdbc":{"$ref":"#/components/schemas/Jdbc"},"mongodb":{"$ref":"#/components/schemas/Mongodb"},"netsuite":{"$ref":"#/components/schemas/NetSuite"},"netsuite_da":{"$ref":"#/components/schemas/NetsuiteDistributed"},"rdbms":{"$ref":"#/components/schemas/Rdbms"},"s3":{"$ref":"#/components/schemas/S3"},"wrapper":{"$ref":"#/components/schemas/Wrapper"},"salesforce":{"$ref":"#/components/schemas/Salesforce"},"tool":{"$ref":"#/components/schemas/Tool"},"file":{"$ref":"#/components/schemas/File"},"filesystem":{"$ref":"#/components/schemas/FileSystem"},"aiAgent":{"$ref":"#/components/schemas/AiAgentConfig"},"guardrail":{"$ref":"#/components/schemas/GuardrailConfig"},"name":{"type":"string","minLength":1,"maxLength":100,"description":"Display name for the import, shown in the flow builder, job history, and error logs.\nDescriptive, unique names indicating the destination system and purpose make large\naccounts easier to manage."},"description":{"type":["string","null"],"description":"Free-text summary of what the import writes and why. Shown in the UI and available to\nAI agents for context; has no effect on execution.","maxLength":5120},"unencrypted":{"type":"object","description":"Custom configuration values stored without encryption and returned in API responses."},"sampleData":{"type":["object","array","string"],"description":"Sample input record used to preview and build the import's mappings."},"distributed":{"type":"boolean","description":"When true, the import uses a distributed adaptor (such as `NetSuiteDistributedImport`)\nthat executes inside the target application rather than on Celigo's servers."},"maxAttempts":{"type":"number","description":"Maximum number of attempts made to deliver a record before it is marked as failed."},"ignoreExisting":{"type":"boolean","description":"When true, records that already exist in the destination system are silently skipped\ninstead of being created or updated — used for create-only operations that must avoid\nduplicates. Existing records are identified by the import's lookup configuration or by\na populated `ignoreExtract` field on the incoming record."},"ignoreMissing":{"type":"boolean","description":"When true, records that do not already exist in the destination system are silently\nskipped instead of producing errors — used for update-only operations."},"idLockTemplate":{"type":["string","null"],"description":"Handlebars template that generates a lock key for each record so records resolving to\nthe same key are not submitted concurrently, preventing duplicate or conflicting writes\nto the same target record."},"dataURITemplate":{"type":["string","null"],"description":"Handlebars template that builds a link back to each record in the destination\napplication's UI. The resolved URL is stored with error records in job history so users\ncan jump straight to the record."},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"blobKeyPath":{"type":"string","description":"Path in the input record that holds the blob key identifying the file content to import.\nAt send time the platform follows this path, retrieves the referenced file from integrator.io\nstorage, and streams its bytes into the outgoing request."},"blob":{"type":"boolean","description":"When true, this import transfers raw file content (blobs) to the destination rather than structured records."},"assistant":{"type":"string","description":"Identifier for the connector assistant used to configure this import."},"deleteAfterImport":{"type":"boolean","description":"When true, the source file is deleted after it is successfully imported."},"assistantMetadata":{"type":"object","description":"Metadata associated with the connector assistant configuration."},"useTechAdaptorForm":{"type":"boolean","description":"When true, the UI presents the full technical adaptor form for this import instead of\nthe simplified assistant form."},"distributedAdaptorData":{"type":"object","description":"Internal state stored by distributed adaptors (such as the NetSuite SuiteApp) for this import."},"filter":{"description":"Filter applied to incoming records before they are sent to the destination system.\nRecords that match continue through the import; records that don't are silently\ndropped. Filter expressions reference the incoming record's fields.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"traceKeyTemplate":{"type":"string","description":"Handlebars template that overrides how each record's unique trace key is generated,\nused to track records through the flow and match errors to records. Trace keys are\ncapped at 256 characters; a longer key is stored truncated from the middle, keeping\nthe beginning and end of the value."},"mockResponse":{"type":"array","description":"Predefined response records used in place of calling the destination system when\ntesting the import, so flows can run without writing real data. Records must be in\nintegrator.io canonical format — the server rejects any other shape with a 422.","items":{"type":"object","properties":{"statusCode":{"type":"integer","description":"HTTP status code the mock returns (e.g. 200, 403)."},"id":{"type":["string","integer"],"description":"Identifier echoed for the mocked record."},"ignored":{"type":"boolean","description":"When true, the mocked record reports as ignored rather than imported."},"dataURI":{"type":"string","description":"Data URI echoed for the mocked record."},"errors":{"type":"array","description":"Mock error entries returned with the record."},"_json":{"type":["object","array"],"description":"Mock response body."},"_headers":{"type":"object","description":"Mock response headers."}}}},"_ediProfileId":{"type":"string","format":"objectId","description":"EDI profile this import uses to generate outbound X12 or EDIFACT documents — it supplies\nthe envelope qualifiers, delimiters, version, and validation rules. Set it when the\nimport produces EDI output; omit it otherwise. Accepted on the file-writing imports —\nFTP, AS2, S3 and HTTP file mode (`http.type: \"file\"`)."},"parsers":{"type":"array","description":"Legacy parser configuration slot. The server initializes this field to an empty array\nand current API writes never populate it; treat it as server bookkeeping rather than a\nsetting to configure."},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the import lifecycle for\ntransformations, validation, and custom result handling beyond what configuration alone\ncan express.","properties":{"preMap":{"type":"object","description":"Hook that runs on each page of records before the import's mappings are applied.\nCommonly used to reshape or filter records ahead of mapping.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postMap":{"type":"object","description":"Hook that runs after the import's mappings are applied but before records are sent\nto the destination system. Commonly used for adjustments that need the mapped\n(destination-shaped) record.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postSubmit":{"type":"object","description":"Hook that runs after the destination system responds, with access to both the\nsubmitted records and the destination's responses. Commonly used to inspect results\nor adjust the response data passed downstream.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postAggregate":{"type":"object","description":"Hook that runs after an aggregated file has been submitted to the destination, for\nfile-based imports that combine records into a single file.","allOf":[{"$ref":"#/components/schemas/Hook"}]}}},"sampleResponseData":{"type":["object","array","string"],"description":"Sample response payload used to preview response mappings and test downstream steps\nwithout calling the destination system."},"responseTransform":{"description":"Transformation that reshapes the destination system's response after records are\nimported, before the response is processed by response mappings and downstream\nsteps. Commonly used to extract relevant fields from verbose API responses.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"modelMetadata":{"type":"object","description":"Metadata about the destination data model captured for this import. Rarely set."},"mapping":{"type":"object","description":"Mapper 1.0 mapping configuration. `fields` maps individual fields; `lists` maps\nrecord lists, each with its own `fields` array. Superseded by Mapper 2.0\n(`mappings`) but still widely used.","properties":{"fields":{"type":"array","description":"Field-level mapping entries applied to each record.","items":{"$ref":"#/components/schemas/MappingField"}},"lists":{"type":"array","description":"List-level mappings, each generating a sublist on the target record.","items":{"type":"object","properties":{"generate":{"type":"string","description":"Target sublist or array path to generate."},"fields":{"type":"array","description":"Field mappings applied within each generated list item.","items":{"$ref":"#/components/schemas/MappingField"}}}}}}},"mappings":{"description":"Field mappings that transform incoming records into the destination system's field\nstructure — renaming fields, converting types, building nested objects, and applying\nformulas or lookups to derive values.","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"lookups":{"description":"Top-level mirror of the adaptor-specific ``lookups`` array. Celigo persists lookups\nin TWO locations on every import resource: ``<adaptor_key>.lookups`` (the typed,\nadaptor-specific definition) and this top-level ``lookups`` array (a permissive copy).","allOf":[{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the shape of the input passed to the import's processing pipeline."},"settingsForm":{"$ref":"#/components/schemas/Form"},"preSave":{"$ref":"#/components/schemas/PreSave"},"settings":{"$ref":"#/components/schemas/Settings"}}},"As2":{"type":"object","description":"Configures how outbound AS2 messages are built for this import. Required when the\n_connectionId field references an AS2 connection. AS2 (Applicability Statement 2) transmits\nEDI and other data securely over HTTP/S using S/MIME encryption and digital signatures; this\nobject names the payload file, the message identifier, retry behavior, and any extra headers.","required":["fileNameTemplate"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies the partner-specific EDI and AS2 configuration.\nWhen set, the import inherits the connector's pre-configured settings; omit to use only\nthe AS2 connection details."},"fileNameTemplate":{"type":"string","description":"Handlebars template that names the file carrying each AS2 message payload. Include a\nuniqueness token such as {{timestamp}} so concurrent or repeated sends do not collide;\ndefaults to file-{{timestamp}} in the form."},"messageIdTemplate":{"type":"string","description":"Handlebars template that generates the Message-ID header for each AS2 message. Placeholders\nare replaced at runtime; the resulting value must be globally unique and follow RFC 5322\nheader formatting. Leave unset to let the platform generate the Message-ID."},"maxRetries":{"type":"number","default":0,"description":"Number of times a failed transmission is retried after transient errors such as network\nfailures or timeouts; does not affect the initial send. The form offers 1–5; set to 0\n(the default) to report failure immediately without retrying."},"headers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header sent with the AS2 message (for example, `content-disposition`). Header names are case-insensitive."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions rendered against the record."}}},"description":"HTTP headers sent with the AS2 message transmission.\nSupports both standard AS2 headers and custom headers required by trading partner agreements."}}},"Dynamodb":{"type":"object","description":"Configuration for DynamoDB imports. Required when the _connectionId field references a\nDynamoDB connection; must not be included for other connection types. putItem writes a full\nitem document; updateItem modifies named attributes of an existing item identified by its\nkey.","required":["region","method","tableName"],"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"],"default":"us-east-1","description":"AWS region hosting the DynamoDB table. Determines the service endpoint used for all\nrequests and must match the region where the table is deployed."},"method":{"type":"string","enum":["putItem","updateItem"],"description":"Write operation performed against the DynamoDB table for each record."},"tableName":{"type":"string","description":"DynamoDB table that receives the imported records.\nMust match an existing table in the target AWS account and region; table names are case-sensitive."},"partitionKey":{"type":"string","description":"Value of the target item's partition key for the updateItem operation; together with\nsortKey (when the table defines one) it identifies the item each record writes to.\nSupports handlebars to reference the incoming record's fields."},"sortKey":{"type":"string","description":"Value of the target item's sort key; supports handlebars. Omit when the table defines only\na partition key."},"itemDocument":{"type":"string","description":"JSON document written to the table for the putItem operation, containing all attribute\nnames and values for the item, including its key attributes. Supports handlebars."},"updateExpression":{"type":"string","description":"DynamoDB update expression naming which attributes to set, add, or remove on the item for\nthe updateItem operation. Only the named attributes change; reference attribute names and\nvalues through the expressionAttributeNames and expressionAttributeValues placeholders."},"conditionExpression":{"type":"string","description":"Condition evaluated against the existing item before the write executes.\nWhen the condition evaluates to false, the operation is aborted and no changes are made."},"expressionAttributeNames":{"type":"string","description":"JSON placeholder-to-name map for attribute names referenced in expressions, for example {\"#N\": \"Name\"}.\nUse it when an attribute name is a reserved word or contains special characters; placeholder keys start with #."},"expressionAttributeValues":{"type":"string","description":"JSON placeholder-to-value map for attribute values referenced in expressions; placeholder\nkeys start with a colon (e.g. \":val\") and each value is a DynamoDB attribute-value object\nsuch as {\"S\": \"text\"}. Supports handlebars."},"ignoreExtract":{"type":"string","description":"Path to the field in the source record used to determine whether the record already exists.\nOnly used when ignoreExisting is set on the import."}},"if":{"properties":{"method":{"const":"updateItem"}},"required":["method"]},"then":{"required":["partitionKey","updateExpression"]}},"Http":{"type":"object","description":"Configuration for HTTP imports.\nRequired whenever the connection referenced by _connectionId has type http, and on\n`NetSuiteHTTPImport` (NetSuite's REST APIs on a `netsuite` connection, where `nsDomainType`\nsupplies the host).","properties":{"sendPostMappedData":{"type":"boolean","default":true,"description":"When true, the request body is the record as reshaped by the import's mapping step. When false, the original pre-mapped record is sent instead, bypassing the mappings for the payload."},"isRest":{"type":"boolean","description":"Internal flag indicating the import was built with the legacy REST form rather than the unified HTTP framework. Set by the form; not configured directly."},"strictHandlebarEvaluation":{"type":"boolean","description":"When true, handlebars expressions that reference a missing field raise an error instead of rendering an empty string, surfacing mapping mistakes early rather than sending blank values."},"formType":{"type":"string","enum":["assistant","http","rest","graph_ql","assistant_graphql"],"description":"Determines the UI form and configuration experience for this import."},"type":{"type":"string","enum":["file","records"],"description":"Controls whether the import sends structured record data or raw file content.\nMost HTTP imports use records; use file only when importing raw file content that will be processed downstream."},"requestMediaType":{"type":"string","enum":["xml","json","csv","urlencoded","form-data","octet-stream","plaintext"],"description":"Content type used to serialize the request body sent to the target API.\nMost REST APIs use json."},"_httpConnectorEndpointIds":{"type":"array","readOnly":true,"items":{"type":"string","format":"objectId"},"description":"HTTP connector endpoint IDs used by this import (multiple endpoints for different request\ntypes or operations). Set by the connector framework; client-supplied values are ignored."},"blobFormat":{"type":"string","enum":["utf8","ucs2","utf-16le","ascii","binary","base64","hex"],"description":"Character encoding format for blob/binary data imports.\nOnly relevant when type is \"file\" or when handling binary content.\n"},"batchSize":{"type":"integer","description":"Maximum number of records submitted per HTTP request, which affects throughput and API rate limiting.\nREST services typically send one record per request; raise it only for batch endpoints or RPC/XML services that accept multiple records, consulting the target API documentation for the optimal value.\n\nThe default varies by API."},"successMediaType":{"type":"string","enum":["xml","json","plaintext"],"description":"Media type expected in successful responses from the target API. Most APIs return json."},"requestType":{"type":"array","items":{"type":"string","enum":["CREATE","UPDATE"]},"description":"Operation type each request performs; for composite (upsert) imports, include both values.\nThe array is positionally aligned with the relativeURI and method arrays — each index maps to one operation — and the existingExtract field determines which operation runs at runtime."},"errorMediaType":{"type":"string","enum":["xml","json","plaintext"],"description":"Media type expected in error responses from the target API. Most APIs return json."},"followRedirects":{"type":"boolean","default":true,"description":"When explicitly false, 3xx responses are not followed — the redirect\nresponse itself (status code, `Location` header, body) is what the\nimport records as the result. Omitted or true follows allowed\nredirects (the default behavior)."},"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":"array","items":{"type":"string"},"description":"Relative URI path for each import request, as an array of strings (never an object); values can include Handlebars expressions for dynamic segments.\nTemplates render against the pre-mapped record — the original input before the Import's mapping step — because URI construction usually needs business identifiers that mappings may rename or remove.\nFor composite (upsert) imports, the array is positionally aligned with method and requestType, and existingExtract decides the index used at runtime: the UPDATE index when its field has a value, the CREATE index when it is empty or missing.\nReference the existing record ID in the UPDATE URI with `{{{data.0.fieldName}}}` (triple braces with the data.0 prefix).\nUse separate array elements per operation rather than `{{#if}}` conditionals inside a single URI."},"method":{"type":"array","items":{"type":"string","enum":["GET","PUT","POST","PATCH","DELETE"]},"description":"HTTP method used for each import request.\nFor composite (upsert) imports, the array is positionally aligned with relativeURI and requestType — e.g. `[\"PUT\", \"POST\"]` with `requestType: [\"UPDATE\", \"CREATE\"]` uses PUT for updates and POST for creates."},"_httpConnectorVersionId":{"type":"string","format":"objectId","readOnly":true,"description":"HTTP connector version used by this import. Set by the connector framework; client-supplied values are ignored (write-tested)."},"_httpConnectorResourceId":{"type":"string","format":"objectId","readOnly":true,"description":"HTTP connector resource used by this import. Set by the connector framework; client-supplied values are ignored (write-tested)."},"_httpConnectorEndpointId":{"type":"string","format":"objectId","readOnly":true,"description":"HTTP connector endpoint used by this import (single endpoint). Set by the connector framework; client-supplied values are ignored (write-tested)."},"body":{"type":"array","items":{"type":["string","null"]},"description":"Request body template for the import — an array of Handlebars template strings\npositionally aligned with the `method` array (e.g. `[\"{{{record}}}\"]`). A `null`\nentry means no template for the method at that position. Each entry is a string:\nwrite a JSON body as a template string (`[\"{\\\"id\\\": \\\"{{record.id}}\\\"}\"]`), not as\na JSON object, because an object entry does not render as JSON at runtime.\nLeave undefined for standard imports — the Import's mapping step transforms the source record and the mapped data is sent as the request body automatically.\nSet it only when the target API requires XML, SOAP, or a custom structure that mappings cannot produce, or when the user explicitly specifies a request body.\nWhen set, the mapping step still runs first and the template renders against the post-mapped record — use `{{record.<mappedField>}}` to reference mapping outputs.\nHand-templated bodies are harder to maintain and debug than mappings; when in doubt, leave this field undefined.\n\nCustom envelopes can combine static header or account fields with per-message\nfield references inside a loop.\n\nStatic wrappers around the mapped record are appropriate for message-bus class envelopes,\nHMAC signature wrappers, integration batch headers,\nnamed single-record array wrappers, and similar destination-required structures.\n\nBatch templates can wrap an envelope, such as a header, version, or marketplace,\naround serialized records.\n\nUse this pattern when each message needs a small number of fields rather than the whole record."},"ignoreEmptyNodes":{"type":"boolean","description":"When true, fields with empty values are stripped from the request body before it is sent,\nso the target API receives only populated fields. Shown as \"Remove empty fields from HTTP\nrequest body\" in the UI."},"existingExtract":{"type":"string","description":"Field name or JSON path that drives the upsert decision for composite imports with both CREATE and UPDATE in requestType.\nWhen the field has a value in the incoming record, the UPDATE operation's relativeURI and method are used; when it is empty or missing, the CREATE operation runs.\nMust match a field populated by an upstream lookup or response mapping (e.g. a destination system ID like \"shopifyCustomerId\"); omit for single-operation imports."},"existingLookupName":{"type":"string","description":"Name of an entry in `lookups` that resolves whether each record already exists in the destination, driving the composite upsert decision when `existingExtract` alone is insufficient (e.g. the check requires a call to the target system). Paired with composite CREATE/UPDATE imports.\n\nUse `existingLookupName` when the incoming record has no destination ID and\nneeds a per-record probe to discover one;\nuse `existingExtract` when the record already carries the ID.\n\nFor single-operation imports,\nuse `ignoreExtract` or `ignoreLookupName` with `ignoreExisting` or\n`ignoreMissing` to skip records instead of routing them.\n\nThe lookup pattern adds one HTTP request per record at runtime,\nmaking it better suited to one-off migrations and low-volume reconciliation than\nhigh-volume recurring syncs."},"ignoreExtract":{"type":"string","description":"Field name on the incoming record that drives the SKIP-vs-PROCESS\ndecision when ``ignoreExisting`` or ``ignoreMissing`` is set.\n\nPairs with either of the two skip-mode flags:\n\n- With ``ignoreExisting: true`` (CREATE-only imports that should\n  avoid duplicates): if ``ignoreExtract`` has a value on the\n  record, the record is treated as already-existing and is\n  skipped; if empty, the record is created.\n- With ``ignoreMissing: true`` (UPDATE-only imports that should\n  avoid creating new records): if ``ignoreExtract`` has a value,\n  the record is treated as existing and is updated; if empty,\n  the record is skipped.\n\n``ignoreExtract`` checks a field that's already on the record —\nzero per-record HTTP overhead.  Use ``ignoreLookupName`` instead\nwhen the record has no field to inspect and the existence check\nneeds a per-record probe against the destination.\n\n**Mutually exclusive with ``ignoreLookupName``**\n\nSet EXACTLY ONE of ``ignoreExtract`` / ``ignoreLookupName`` when\n``ignoreExisting`` or ``ignoreMissing`` is enabled.\n\n**When not to set this field**\n\nOnly valid for single-operation imports with either\n``ignoreExisting: true`` or ``ignoreMissing: true`` set.\nComposite (upsert) imports — those with both ``CREATE`` and\n``UPDATE`` in ``requestType`` — use ``existingExtract`` /\n``existingLookupName`` instead, because they route records\nbetween two write paths rather than skipping them."},"endPointBodyLimit":{"type":"integer","description":"Maximum size limit for the request body in bytes.\nUsed to enforce API-specific size constraints.\n"},"headers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header to include with each import request."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions rendered against the pre-mapped record."}}},"description":"Custom HTTP headers included with import requests; values can contain Handlebars expressions.\nValue templates render against the pre-mapped record (the original input before the Import's mapping step) — use `{{record.<field>}}` to reference fields as they appear in the upstream source."},"response":{"type":"object","properties":{"resourcePath":{"type":"array","items":{"type":"string"},"description":"Per-operation JSON path to the resource collection in each\nresponse.  Outer-array length MUST match ``requestType``\nlength: 1 entry for single-op imports, N entries for an\nN-operation composite import (duplicate the value when\noperations agree).  See the ``response`` object docs\nabove for the full positional-array contract.\n\nRequired when ``batchSize > 1`` because the platform needs\nto know where the array of result records lives inside the\nresponse envelope.  When the API returns a bare array at\nthe top level, leave this field unset.\n\nEach entry is a DOT-PATH STRING (e.g. ``\"data.results\"``,\n``\"items\"``) — do NOT split paths into segments across\narray elements."},"resourceIdPath":{"type":"array","items":{"type":["string","null"]},"description":"Per-operation JSON path to the unique ID field within each\nrecord in the response.  Outer-array length MUST match\n``requestType`` length — duplicate the value when both\noperations agree (the common case) rather than collapsing\nto a 1-entry array.\n\nEach entry is a DOT-PATH STRING, or null for an operation\nwith no ID path configured. When not specified, the platform\nlooks for standard `id` or `_id` fields automatically."},"successPath":{"type":"array","items":{"type":"string"},"description":"Per-operation JSON path to a field that indicates whether\nthe API call succeeded.  Outer-array length MUST match\n``requestType`` length — duplicate the value when both\noperations agree rather than collapsing to a 1-entry\narray on a composite import.\n\nUse this when the API returns HTTP 200 for everything and\nsignals success / failure through a body field.  Pairs with\n``successValues`` to define which values at this path mean\nsuccess.\n\nEach entry is a DOT-PATH STRING."},"successValues":{"type":"array","items":{"type":"array","items":{"type":"string"}},"description":"Per-operation list of values at ``successPath`` that mean\nsuccess.  TWO levels of array nesting:\n\n- Outer array: one entry per ``requestType`` index.\n  Length MUST match ``requestType`` length — duplicate\n  the inner array when both operations agree rather than\n  collapsing to a 1-entry outer array on a composite\n  import.\n- Inner array: the list of acceptable success values for\n  that operation.\n\nAll comparisons are STRING comparisons regardless of the\nactual response value type — the platform stringifies\nnumbers, booleans, etc. before comparing."},"failPath":{"type":"array","items":{"type":"string"},"description":"Per-operation JSON path to a field that signals failure\neven when HTTP returns 200.  Outer-array length MUST\nmatch ``requestType`` length — duplicate when both\noperations agree.  Same array semantics as ``successPath``.\n\nSome APIs use only ``successPath`` (failure = absence of\nsuccess); others publish both an explicit success indicator\nand an explicit failure indicator.  When both are\navailable, set both — the platform evaluates ``successPath``\nfirst and falls through to ``failPath`` only when the\nsuccess check is inconclusive."},"failValues":{"type":"array","items":{"type":"array","items":{"type":"string"}},"description":"Per-operation list of values at ``failPath`` that mean\nfailure.  Same two-level array shape and length contract\nas ``successValues`` — outer length MUST match\n``requestType`` length, duplicate the inner array on\ncomposite imports when both operations agree.\n\nOften used as the inverse of ``successValues`` (e.g.\n``successValues: [[\"true\"]]`` paired with\n``failValues: [[\"false\"]]``)."},"errorPath":{"type":"string","description":"JSON path to the error message inside the response body.\n\n**Note**: this field is NOT a per-operation array — it's a\nsingle string applied to every operation.  Most APIs use\nthe same error-message field shape for both UPDATE and\nCREATE responses, so the platform doesn't expose a per-op\noverride here."},"allowArrayforSuccessPath":{"type":"boolean","description":"When true, allows array values at successPath during success evaluation."},"hasHeader":{"type":"boolean","description":"When true, treats the first record in the response as a header row (for CSV responses)."}},"description":"Configuration for parsing and interpreting HTTP responses returned\nfrom each write request the import issues.\n\n**Critical: positional per-operation arrays**\n\nEvery field inside ``response`` (``resourcePath``, ``resourceIdPath``,\n``successPath``, ``successValues``, ``failPath``, ``failValues``)\nis an ARRAY whose outer length is positionally aligned with\n``requestType`` / ``method`` / ``relativeURI``.  This is the\nSAME positional-alignment contract as those three sibling\narrays — see ``relativeURI`` docs for the full explanation.\n\nEach outer-array index describes how to parse the response from\nthe operation at that same index in ``requestType``:\n\n- **Single-operation import** (``requestType: [\"CREATE\"]``):\n  every response field is a 1-element array.\n  ``successPath: [\"ok\"]`` means \"for the one operation,\n  look at path ``ok`` to detect success\".\n- **Composite upsert** (``requestType: [\"UPDATE\", \"CREATE\"]``):\n  every response field is a 2-element array.  Index 0 parses\n  the UPDATE response, index 1 parses the CREATE response.\n\n**successValues / failValues — array of arrays**\n\n``successValues`` and ``failValues`` carry an extra array level\nbecause each operation can have MULTIPLE acceptable values:\n\n- Outer array: one entry per operation (positional, as above).\n- Inner array: the list of acceptable values for that operation.\n\nExample:\n```json\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"successPath\": [\"ok\", \"ok\"],\n\"successValues\": [[\"true\", \"1\"], [\"true\"]]\n```\nMeans: the UPDATE response is successful when ``ok`` is either\n``\"true\"`` or ``\"1\"``; the CREATE response is successful only\nwhen ``ok`` is ``\"true\"``.\n\n**Always duplicate when operations agree (REQUIRED for composite)**\n\nThe outer-array length MUST match ``requestType`` length on\nevery response field.  When the UPDATE and CREATE responses\nof the same connector look the same (which is the common\ncase — most APIs return the same shape from both endpoints),\nDUPLICATE the entry so the array has the same length as\n``requestType``.  Do NOT collapse to a 1-entry array on a\ncomposite import.\n\n```json\n// ✅ Correct — explicit per-operation entries, even when identical\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data.id\", \"data.id\"],\n\"successPath\": [\"success\", \"success\"],\n\"successValues\": [[\"true\"], [\"true\"]]\n```\n\n```json\n// ❌ Wrong — 1-entry array on a composite import\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data.id\"],\n\"successPath\": [\"success\"],\n\"successValues\": [[\"true\"]]\n```\n\nWhy duplicate even when they agree:\n\n- **UI parity.**  The Celigo UI renders one response-handling\n  form per ``requestType`` index.  A 1-entry array on a\n  composite import leaves the second operation's form panel\n  empty, which looks like a half-built config to the user\n  reviewing the saved import — even if it works at runtime.\n- **Symmetric shape rule.**  ``relativeURI`` / ``method`` /\n  ``requestType`` REQUIRE the entry count to match across\n  all three (the runtime decider uses positional alignment).\n  Making ``response.*`` follow the same \"outer length ==\n  requestType length\" rule means there's ONE shape rule to\n  remember, not two.\n- **Unambiguous intent.**  A 2-entry duplicated array\n  documents in the saved config that the developer\n  considered both operations.  A 1-entry array could mean\n  \"both ops use this\" or could mean \"I forgot to fill in\n  the second op.\"  Duplication eliminates the ambiguity.\n\nWhen operations DIFFER (rare — different response shapes\nper endpoint), set distinct values at each index.  Either\nway the outer length always matches ``requestType``.\n\n**Common llm mistake (DO not do THIS)**\n\nDo NOT split a single dot-path into array segments thinking the\nouter array represents path segments:\n\n```json\n// WRONG — the LLM expressed \"data.id\" as path segments\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data\", \"id\"]\n```\n\nThis is interpreted by the platform as: UPDATE response's id is\nat path ``data``, CREATE response's id is at path ``id`` — almost\nnever what the user wants.  The CORRECT shape is the single\ndot-path expressed as ONE string per operation, duplicated to\nmatch ``requestType`` length:\n\n```json\n\"requestType\": [\"UPDATE\", \"CREATE\"],\n\"resourceIdPath\": [\"data.id\", \"data.id\"]\n```"},"_asyncHelperId":{"type":"string","format":"objectId","description":"Reference to an AsyncHelper resource that polls an asynchronous\ndestination API on this import's behalf.\n\nSet this ONLY when the destination API is genuinely asynchronous — it\nacknowledges the submitted payload (HTTP 202, a job ticket) and ingests\nit in the background, so completion must be polled for (e.g. bulk\nimage/file ingestion, large bulk-load endpoints). Most imports are\nsynchronous and need NO async helper; adding one to a synchronous\ndestination just adds polling overhead plus a status and result export\nto maintain. When in doubt, leave it unset.\n\nThe referenced helper bundles the polling config plus a required status\nexport (polled to check progress) and a result export (fetches the\nfinal payload). An import configured with an async helper cannot carry\nits own transform, output filter, or preSavePage hook — put that\nprocessing in the result export instead."},"ignoreLookupName":{"type":"string","description":"Name of an entry in `lookups` used to check whether each record already exists, so `ignoreMissing`/`ignoreExisting` can skip it. Use it instead of `ignoreExtract` when existence must be resolved by a call to the target system rather than read from a field on the incoming record."},"lookups":{"description":"Lookups referenced by `existingLookupName`/`ignoreLookupName` to resolve record existence against the destination system at runtime.","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia ``lookupName`` and Handlebars expressions reference it via\n``{{lookup.name}}``. Must be unique within this\nresource's ``lookups`` array.\n"},"method":{"type":"string","enum":["GET","POST","PUT","PATCH","DELETE"],"description":"HTTP method used to issue the lookup request. ``GET`` and\n``POST`` are most common. Omit when using a static ``map`` or\n``_lookupCacheId``.\n"},"relativeURI":{"type":"string","description":"HTTP path (relative to the connection's base URL) that\nresolves the lookup. Use Handlebars with triple braces to\ninject values from the incoming record — e.g.\n``/customers?email={{{email}}}`` or\n``/accounts/{{{accountId}}}``.\n"},"postBody":{"type":"string","description":"Request body template for POST / PUT / PATCH lookup methods.\nTypically a JSON string with Handlebars placeholders —\n``{\"email\":\"{{{email}}}\"}``. Ignored for GET / DELETE.\n"},"headers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}},"description":"Optional custom headers to attach to the lookup request.\nUseful for endpoints that need a different Accept header or\nan auxiliary auth token separate from the connection-level\nheaders.\n"},"extract":{"type":"string","description":"JSONPath expression that selects the lookup value from the\nHTTP response body. ``$.`` prefix optional. Examples:\n``$.data[0].id``, ``$.results.primaryKey``, ``id``.\n"},"_lookupCacheId":{"type":"string","format":"objectId","description":"Optional reference to a LookupCache resource. When set, the\nlookup reads from the cache instead of issuing a live HTTP\nrequest — useful for stable reference data that should be\npre-loaded.\n"},"map":{"type":["object","null"],"description":"Optional static key→value object evaluated BEFORE the HTTP\nrequest. When the incoming value matches a key, the mapped\nvalue is returned without hitting the remote endpoint. Null\non dynamic lookups with no static map.\n"},"default":{"type":["string","null"],"description":"Value returned when the HTTP request returns no usable\nmatch. When omitted and ``allowFailures`` is false, an\nunmatched lookup halts the record. Stored as null when no\nfallback is configured.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, a lookup miss (no matching response and no\n``default``) resolves to ``null`` and the import continues.\nWhen false (default), a miss halts the record. May be\nstored as null (treated as unset).\n"},"useDefaultOnMultipleMatches":{"type":"boolean","description":"Controls behaviour when the response contains more than one\nmatch. When true, the ``default`` value is returned instead\nof raising an ambiguous-match error. When false (default),\nmultiple matches are treated as a failure.\n"}}},"type":"array"}}},"Ftp":{"type":"object","description":"Defines where files are written on 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 target folder and the file naming comes from\nthe import's file configuration.","required":["directoryPath"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies partner-specific B2B settings for this import.\nWhen set, the import inherits the connector's pre-configured settings; omit to use only\nthe FTP connection details."},"directoryPath":{"type":"string","description":"Directory on the server where imported files are written, either absolute or relative to\nthe login directory; the FTP user must have write permission on it. Use forward slashes\nregardless of server OS — paths are case-sensitive on UNIX/Linux servers. Supports\nhandlebars templates."},"fileName":{"type":"string","description":"Name of the file written to the server, including its extension; do not include directory\nseparators — the location comes from directoryPath. Supports handlebars placeholders such\nas items-{{timestamp}}.csv to generate a unique name per run."},"inProgressFileName":{"type":"string","description":"Temporary name the file carries while its upload is in progress, preventing other systems\nfrom processing a partially transferred file; it is renamed to its final name once the\nupload completes. Include a handlebars uniqueness token (e.g. {{timestamp}}) when one file\nis written per flow run."},"backupDirectoryPath":{"type":"string","description":"Directory on the same server where a copy of each written file is retained after a\nsuccessful import; if omitted, no server-side backup is kept. Supports static paths or\nhandlebars templates."}}},"Jdbc":{"type":"object","description":"Configuration for JDBC import operations. Defines how data is written to a database\nvia a JDBC connection.\n\n**Query type determines which fields are required**\n\n| queryType        | Required fields              | Do NOT set        |\n|------------------|------------------------------|-------------------|\n| [\"per_record\"]   | query (array of SQL strings) | bulkInsert        |\n| [\"per_page\"]     | query (array of SQL strings) | bulkInsert        |\n| [\"bulk_insert\"]  | bulkInsert object            | query             |\n| [\"bulk_load\"]    | bulkLoad object              | query             |\n\n**Critical:** query IS AN ARRAY OF STRINGS\nThe query field must be an array of plain strings, NOT a single string and NOT an array of objects.\nCorrect: [\"INSERT INTO users (name) VALUES ('{{{name}}}')\"]\nWrong: \"INSERT INTO users ...\"\nWrong: [{\"query\": \"INSERT INTO users ...\"}]","required":["queryType"],"properties":{"query":{"type":"array","items":{"type":"string"},"description":"Array of SQL query strings to execute. REQUIRED when queryType is [\"per_record\"] or [\"per_page\"].\n\nEach element is a complete SQL statement as a plain string. Typically contains a single query.\n\nUse Handlebars {{fieldName}} syntax to inject values from incoming records.\n\n**Format — array of strings**\n- CORRECT: [\"INSERT INTO users (name, email) VALUES ('{{{name}}}', '{{{email}}}')\"]\n- WRONG: \"INSERT INTO users ...\"  (not an array)\n- WRONG: [{\"query\": \"INSERT INTO users ...\"}]  (objects are invalid — causes Cast error)\n\n**Examples**\n- INSERT: [\"INSERT INTO users (name, email) VALUES ('{{{name}}}', '{{{email}}}')\"]\n- UPDATE: [\"UPDATE inventory SET qty = {{{quantity}}} WHERE sku = '{{{sku}}}'\"]\n- MERGE/UPSERT: [\"MERGE INTO target USING (SELECT CAST(? AS VARCHAR) AS email) AS src ON target.email = src.email WHEN MATCHED THEN UPDATE SET name = ? WHEN NOT MATCHED THEN INSERT (name, email) VALUES (?, ?)\"]\n\n**String vs numeric values in handlebars**\n- Use triple braces so the raw value is emitted; double braces emit the value already single-quoted, so wrapping them in quotes double-quotes it.\n- Strings: supply the quotes yourself — '{{{name}}}'\n- Numbers: no quotes — {{{quantity}}}"},"queryType":{"type":"array","items":{"type":"string","enum":["bulk_insert","per_record","per_page","bulk_load"]},"description":"Execution strategy for the SQL operation. REQUIRED. Must be an array with one value.\n\n**Decision tree**\n\n1. If UPDATE or UPSERT/MERGE → [\"per_record\"] (set query field)\n2. If INSERT with \"ignore existing\" / \"skip duplicates\" / match logic → [\"per_record\"] (set query field)\n3. If pure INSERT with no duplicate checking → [\"bulk_insert\"] (set bulkInsert object)\n4. If high-volume bulk load → [\"bulk_load\"] (set bulkLoad object)\n\n**Critical relationship to other fields**\n| queryType        | REQUIRES              | DO NOT SET   |\n|------------------|-----------------------|--------------|\n| [\"per_record\"]   | query (array)         | bulkInsert   |\n| [\"per_page\"]     | query (array)         | bulkInsert   |\n| [\"bulk_insert\"]  | bulkInsert object     | query        |\n| [\"bulk_load\"]    | bulkLoad object       | query        |\n\n**Examples**\n- Per-record upsert: [\"per_record\"]\n- Bulk insert: [\"bulk_insert\"]\n- Bulk load: [\"bulk_load\"]\n"},"bulkInsert":{"type":"object","description":"Bulk insert configuration. REQUIRED when queryType is [\"bulk_insert\"]. DO NOT SET when queryType is [\"per_record\"].\n\nEnables efficient batch insertion of records into a database table without per-record SQL.\n","properties":{"tableName":{"type":"string","description":"Target database table name for bulk insert. REQUIRED.\nCan include schema qualifiers (e.g., \"schema.tableName\").\n"},"batchSize":{"type":"string","description":"Number of records per batch during bulk insert.\nLarger values improve throughput but use more memory.\nCommon values: \"1000\", \"5000\".\n"}}},"bulkLoad":{"type":"object","description":"Bulk load configuration. REQUIRED when queryType is [\"bulk_load\"]. Uses database-native bulk loading for maximum throughput.\n","properties":{"tableName":{"type":"string","description":"Target database table name for bulk load. REQUIRED.\nCan include schema qualifiers (e.g., \"schema.tableName\").\n"},"primaryKeys":{"type":["array","null"],"items":{"type":"string"},"description":"Primary key column names for upsert/merge during bulk load.\nWhen set, existing rows matching these keys are updated; non-matching rows are inserted.\nExample: [\"id\"] or [\"order_id\", \"product_id\"] for composite keys.\n"},"overrideMergeOrInsertQuery":{"type":"boolean","description":"When true, a custom merge/insert query replaces the auto-generated statement for the\nbulk load, enabling ignore-existing logic or conditional updates."}}},"lookups":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia `lookupName` and Handlebars expressions reference it via\n`{{lookup \"name\" value}}`. Must be unique within this\nresource's `lookups` array."},"query":{"type":"string","description":"SQL query for the lookup (e.g., \"SELECT id FROM users WHERE email = '{{{email}}}'\")."},"extract":{"type":"string","description":"JSONPath-style expression that selects the value from the\nquery result row (e.g. `id`, `details.price`). Omit when\nthe query returns a single scalar."},"_lookupCacheId":{"type":"string","format":"objectId","description":"Optional reference to a LookupCache resource. When set, the\nlookup reads from the cache instead of issuing a live SQL\nquery — useful for stable reference data that should be\npre-loaded."},"allowFailures":{"type":"boolean","description":"When true, a lookup miss (no matching row and no `default`)\nresolves to `null` and the import continues. When false\n(default), a miss halts the record."},"map":{"type":"object","description":"Optional static key→value object evaluated BEFORE the SQL\nquery. When the incoming value matches a key, the mapped\nvalue is returned without querying the database."},"default":{"type":"string","description":"Value returned when the query matches no rows. When omitted\nand `allowFailures` is false, an unmatched lookup halts the\nrecord."}}},"description":"Lookup definitions executed against the JDBC connection. Each entry\nruns a `SELECT` and extracts a value used by field mappings\n(referenced via `lookupName`) or Handlebars expressions\n(referenced via `{{lookup \"name\" value}}`)."}}},"Mongodb":{"type":"object","description":"Configuration for MongoDB imports. Contains only the MongoDB-specific properties; import-level fields such as ignoreExisting, ignoreMissing, name, and description are set on the parent import, not here.","required":["method","collection"],"properties":{"method":{"type":"string","enum":["insertMany","updateOne"],"description":"Write operation performed against the MongoDB collection for each record."},"collection":{"type":"string","description":"MongoDB collection that receives the imported records.\nCollection names are case-sensitive and must not start with the reserved \"system.\" prefix."},"filter":{"type":"string","description":"MongoDB query filter that selects the document each updateOne applies to; required when\nmethod is updateOne. Supports standard MongoDB query operators and dot notation, and\nhandlebars placeholders to reference the incoming record's fields."},"document":{"type":"string","description":"Content of the document written to the collection (used when method is insertMany), as a JSON string that can include nested documents and arrays and handlebars placeholders."},"update":{"type":"string","description":"Modifications applied to matching documents (used when method is updateOne), using MongoDB update operators such as $set, $inc, or $push.\nA complete document without operators replaces the matched document entirely."},"upsert":{"type":"boolean","description":"When true, inserts a new document built from the update criteria if no existing document matches the filter.\nWhen false, records with no matching document are skipped and nothing is inserted."},"ignoreExtract":{"type":"string","description":"Path to the field in the source record that identifies existing records. When that field has a value, the record is treated as existing and skipped.\nOnly used when the import-level ignoreExisting flag is true; do not set it otherwise.\nEnclose field names containing special characters in square brackets (for example, [vendor-code]), and reference array items with an index, such as items[0].id."},"ignoreLookupFilter":{"type":"string","description":"JSON-stringified MongoDB query filter used to find existing documents in the target collection when the import-level ignoreExisting flag is true.\nIf the query matches a document, the incoming record is treated as existing and skipped.\nUse Handlebars placeholders to reference values from the incoming record, for example \"{\\\"email\\\":\\\"{{email}}\\\"}\".\nUnlike ignoreExtract, which only checks whether a field on the incoming record has a value, this filter queries the MongoDB collection itself."}},"if":{"properties":{"method":{"const":"updateOne"}},"required":["method"]},"then":{"required":["filter"]}},"NetSuite":{"type":"object","description":"Configuration for NetSuite imports (legacy adaptor; use netsuite_da / NetSuiteDistributedImport for SuiteApp 2.0).","properties":{"lookups":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique name for this lookup, referenced from mappings and handlebars templates with `{{lookup 'name' value}}`."},"recordType":{"type":"string","description":"NetSuite record type searched by a dynamic lookup (for example, `customer`, `salesOrder`). Set with `searchField` or `expression`; omit for a static `map` lookup."},"searchField":{"type":"string","description":"NetSuite field the dynamic lookup searches on (for example, `externalid`). Must be a searchable field on `recordType`. Use this or `expression`."},"expression":{"type":"string","description":"NetSuite search expression evaluated at runtime to select matching records (for example, `[\"recipient\",\"is\",\"123\"]`). Use this or `searchField`."},"resultField":{"type":"string","description":"Field on the matched NetSuite record whose value the lookup returns (for example, `internalid`).\nSupports dot notation for nested fields."},"map":{"type":["object","null"],"description":"Static lookup table with input values as keys and their corresponding output values. Use instead of a NetSuite search for fixed value translations. The platform stores `null` here on search-driven lookups."},"default":{"type":["string","null"],"description":"Fallback value returned when the lookup finds no match. Used together with `allowFailures`. Stored as `null` when not configured."},"allowFailures":{"type":"boolean","description":"When true, an unmatched lookup falls back to `default` and processing continues; when false, an unmatched lookup fails the record."},"_id":{"type":"object","description":"Server-assigned unique identifier for this lookup entry.","readOnly":true}}},"description":"Lookup definitions used to resolve reference values during the import, using either a static `map` or a dynamic NetSuite record search."},"operation":{"type":"string","enum":["add","update","addupdate","attach","detach","delete"],"description":"Operation to perform on NetSuite records, as a plain string (the API lowercases the value).\nFor `update`, `addupdate`, and `delete`, also set internalIdLookup so existing records can be found; for `add` with ignoreExisting, set internalIdLookup to check for duplicates before creating.\nDefault to `addupdate` when the intent is \"sync\", \"upsert\", or \"create or update\"; use `add` for plain \"create\" or \"insert\".\nDo not wrap the value in an object like `{\"type\": \"addupdate\"}` — that fails validation."},"isFileProvider":{"type":"boolean","description":"When true, enables file operations against NetSuite — browsing, uploading, downloading, updating, and deleting files.\nWhen false or omitted, file-related operations are unavailable through this import."},"customFieldMetadata":{"type":"object","description":"Metadata describing the custom fields defined on the target NetSuite record type, used to validate and process custom field data during the import."},"recordType":{"type":"string","description":"NetSuite record type this import writes to (e.g. customer, salesOrder, invoice).\nUse the exact NetSuite internal record type identifier; both standard and custom record types are supported."},"recordTypeId":{"type":"string","description":"The unique identifier specifying the record type within NetSuite. Determines the applicable schema, fields, validation rules, and processing logic. Use exact NetSuite record type identifiers such as standard types or custom record IDs."},"retryUpdateAsAdd":{"type":"boolean","description":"When true, an update that fails because the target record does not exist is automatically retried as an add.\nWhen false or unset, update failures error immediately without retrying.\nMay create duplicate records if the update failed for a reason other than a missing record."},"batchSize":{"type":"number","description":"Number of records sent to NetSuite per API call.\nLarger batches reduce the number of calls but increase memory use and timeout risk per call."},"internalIdLookup":{"type":"object","properties":{"extract":{"type":"string","description":"Path used to extract the value for the internal ID lookup."},"searchField":{"type":"string","description":"The NetSuite record field used as the key attribute for the internal ID lookup search. Should be a unique or indexed field to ensure accurate matches. Must correspond to a valid, searchable field on the target record type."},"expression":{"type":"string","description":"NetSuite search expression that filters which records match the lookup.\nSupports logical operators (AND, OR, NOT), comparison operators, and nested conditions, evaluated at runtime against current data."}},"description":"Configuration for locating existing NetSuite records by internal ID.\nRequired when operation is `update`, `addupdate`, or `delete`; also used with `add` plus ignoreExisting to check for duplicates."},"preferences":{"type":"object","properties":{"ignoreReadOnlyFields":{"type":"boolean","description":"When true, silently skips read-only fields during update operations instead of raising errors."},"warningAsError":{"type":"boolean","description":"When true, treats NetSuite warnings as errors, causing the operation to fail immediately."},"skipCustomMetadataRequests":{"type":"boolean","description":"When true, skips custom metadata requests during NetSuite API operations to improve performance."}},"description":"Preferences that control how NetSuite handles the import operation."},"file":{"type":"object","properties":{"name":{"type":"string","description":"Filename for the file in the NetSuite File Cabinet, without any folder or path information.\nMust be unique within its folder; include the file extension (e.g. .pdf, .csv)."},"fileType":{"type":"string","description":"NetSuite file type that governs how the file is processed, stored, and displayed.\nMust match the actual content format of the file."},"folder":{"type":"string","description":"Folder in the NetSuite File Cabinet where the file is stored, as a numeric folder ID or a folder path.\nUpdating this on an existing file moves the file to the specified folder."},"folderInternalId":{"type":"string","description":"Internal ID of the File Cabinet folder where the file is stored.\nMust reference an existing folder; changing it moves the file to a different folder."},"internalId":{"type":"string","description":"NetSuite internal ID of an existing file, used to target it for retrieval, updates, or deletion.\nAssigned by NetSuite when the file is created and cannot be changed."},"backupFolderInternalId":{"type":"string","description":"Internal ID of the File Cabinet folder where backup files are stored."}},"description":"Configuration for the file to upload to or update in the NetSuite File Cabinet."}},"if":{"not":{"propertyNames":{"enum":["preferences","lookups"]}}},"then":{"required":["operation"],"if":{"properties":{"isFileProvider":{"const":true}},"required":["isFileProvider"]},"else":{"required":["recordType"]}}},"NetsuiteDistributed":{"type":"object","description":"Configuration for NetSuite Distributed (SuiteApp 2.0) import operations — the primary sub-schema for the NetSuiteDistributedImport adaptorType. The API field name is \"netsuite_da\".\noperation and recordType are required. The server may attach empty bookkeeping stubs of\nthis object (empty lookups/mapping, or missingOrCorruptedDAConfig) to imports of other\nadaptor types; stubs omit these fields. Set internalIdLookup so existing records\ncan be found when operation is update, addupdate, or delete.","properties":{"operation":{"type":"string","enum":["add","update","addupdate","attach","detach","delete"],"description":"Operation to perform on the target NetSuite record, as a plain string. Required.\nFor `update`, `addupdate`, and `delete`, also set internalIdLookup so existing records can be found."},"recordType":{"type":"string","description":"NetSuite record type to import into (e.g. \"customer\", \"salesorder\", \"customrecord_myrecord\" for custom record types). Required.\nMust match a valid NetSuite record type identifier."},"recordIdentifier":{"type":"string","description":"Custom record identifier, used to identify the specific record type when\nimporting into custom record types.\n"},"restletVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"Version of the NetSuite RESTlet to use, as a plain string.\nDefaults to \"suiteapp2.0\" when useSS2Restlets is true, \"suitebundle\" otherwise; rarely needs to be set explicitly for modern integrations.\nThe version is fixed when the step is created — the Advanced selector is disabled on existing steps, and migrating an existing step to a different version means recreating or cloning it."},"useSS2Restlets":{"type":"boolean","description":"When true, uses SuiteScript 2.0 RESTlets and restletVersion defaults to \"suiteapp2.0\".\nDefaults to true for modern integrations.\nSet at step creation together with restletVersion — the same creation-time-only constraint applies (see restletVersion)."},"missingOrCorruptedDAConfig":{"type":"boolean","description":"When true, the Distributed Adaptor configuration is missing or corrupted.\nSet by the system — do not set manually."},"batchSize":{"type":"number","description":"Number of records to process per batch. Controls how many records are sent\nto NetSuite in a single API call. Typical values: 50-200.\n"},"internalIdLookup":{"type":"object","description":"Configuration for looking up existing NetSuite records to match against incoming data.\nRequired when operation is \"update\", \"addupdate\", or \"delete\".","properties":{"extract":{"type":"string","description":"Path in the source record to extract the lookup value from (e.g. \"internalId\" or \"externalId\")."},"searchField":{"type":"string","description":"NetSuite field to search against (e.g. \"externalId\", \"email\", \"name\", \"tranId\")."},"operator":{"type":"string","description":"Comparison operator for the lookup (e.g. \"is\", \"contains\", \"startswith\")."},"expression":{"type":"string","description":"NetSuite search expression for complex lookup conditions, used for multi-field or conditional lookups."}}},"hooks":{"type":"object","description":"Script hooks for custom processing at different stages of the import.\nEach hook references a SuiteScript file and function.\n","properties":{"preMap":{"type":"object","description":"Runs before field mapping is applied.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}},"postMap":{"type":"object","description":"Runs after field mapping, before submission to NetSuite.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}},"postSubmit":{"type":"object","description":"Runs after the record is submitted to NetSuite.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}}}},"mapping":{"type":"object","description":"NetSuite-specific Mapper 1.0 field mappings (the \"DA\" dialect).\nUsed by `NetSuiteDistributedImport` exclusively — NetSuite's\nDistributed Adaptor cannot consume generic v1 mappings or the\nMapper 2.0 shape because it needs sublist, subrecord, and\nconditional lookup semantics the generic schemas don't model.\n\nField-level shape: `fields` for body columns, `lists` for\nsublists / line-item arrays.","properties":{"fields":{"type":"array","items":{"type":"object","properties":{"extract":{"type":["string","null"],"description":"Path in the source record to extract the value from.\nUse dot notation for nested fields (e.g., \"address.city\").\nThe platform stores `null` here on fields driven by\n`hardCodedValue` instead of an extract path.\n"},"generate":{"type":"string","description":"The NetSuite field ID to write the value to (e.g., \"companyname\", \"email\", \"subsidiary\").\n"},"hardCodedValue":{"type":"string","description":"A static value to always use instead of extracting from source data.\nMutually exclusive with extract.\n"},"lookupName":{"type":"string","description":"Reference to a lookup defined in netsuite_da.lookups by name."},"dataType":{"type":"string","description":"Data type hint for the field value (e.g., \"string\", \"number\", \"date\", \"boolean\").\n"},"internalId":{"type":"boolean","description":"When true, the value is a NetSuite internal ID reference."},"immutable":{"type":"boolean","description":"When true, this field is only set on record creation, not on updates."},"discardIfEmpty":{"type":"boolean","description":"When true, skip this field mapping if the extracted value is empty."},"extractDateFormat":{"type":"string","description":"Date format of the extracted value (e.g., \"MM/DD/YYYY\", \"ISO8601\").\nUsed to parse date strings from source data.\n"},"extractDateTimezone":{"type":["string","null"],"description":"Timezone of the extracted date value (e.g., \"America/New_York\"). Stored as `null` when not configured."},"subRecordMapping":{"type":"object","description":"Nested Mapper 1.0 mapping for a NetSuite subrecord\nembedded in this field.  A subrecord is a child\nrecord that only exists inside a parent (e.g. the\nInventory Detail on a serialized-item line of a\nsales order); NetSuite reaches it through the\nparent.\n\n**Recursive structure.**  The nested `mapping`\nobject has the same `{fields, lists}` shape as the\ntop-level `netsuite_da.mapping`.  Recursion is\nallowed but rare.","properties":{"recordType":{"type":"string","description":"NetSuite's internal record-type id of the\nSUBRECORD (not the parent).  E.g.\n`inventorydetail`, `itempricing`,\n`mainaddress`.\n"},"jsonPath":{"type":"string","description":"JSONPath into the source record that selects the\ndata feeding this subrecord.  Semantics depend\non placement:\n* Line-level (on `lists[].fields[].`\n  `subRecordMapping`) — often `$` to reuse\n  the enclosing sublist line's row, or a path\n  into a nested array on that row.\n* Body-level (on `fields[].subRecordMapping`)\n  — a path into the source record root.\nDefaults to `$`.\n"},"mapping":{"type":"object","description":"Nested `{fields, lists}` mapping for the\nsubrecord.  Identical shape to the top-level\n`netsuite_da.mapping`.  Subrecords often have\nempty `fields` and only a sublist (e.g.\n`inventorydetail` → `inventoryassignment`).\n"},"lookups":{"type":"array","description":"Subrecord-scoped lookups.  Same shape as the\ntop-level `netsuite_da.lookups`.  Fields inside\nthis `subRecordMapping.mapping` reference them\nvia `lookupName`.  Often `[]`.\n"},"referenceFieldId":{"type":"string","description":"Secondary reference-field identifier used on a\nsmall fraction of subrecord blocks in production\n(~3%).  Exact semantics are not publicly\ndocumented by Celigo — match the value on\nexisting resources when present; do NOT\nsynthesize one.  Leave unset unless a template\nclearly requires it.\n"}}},"conditional":{"type":"object","description":"Conditional logic for when to apply this field mapping\n(\"Only perform mapping when\" in the UI). Lookup-based\nconditions evaluate the lookup named by `lookupName`,\nwhich must be defined on this import.\n","properties":{"lookupName":{"type":"string","description":"Lookup to evaluate for the condition. Must exactly match the\n`name` of a lookup defined on this import."},"when":{"type":"string","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","ignore_if_set"],"description":"Condition that controls when this field mapping is applied."}}}}},"description":"Body-level field mappings. Each entry maps a source field to a NetSuite body field.\n"},"lists":{"type":"array","items":{"type":"object","properties":{"generate":{"type":"string","description":"The NetSuite sublist ID (e.g., \"item\" for sales order line items,\n\"addressbook\" for address sublists).\n"},"jsonPath":{"type":"string","description":"LEGACY — do not set. Nominally the JSON path to the array of sublist records, but NetSuite DA ignores a top-level jsonPath here."},"fields":{"type":"array","items":{"type":"object","properties":{"extract":{"type":["string","null"],"description":"Path from the source record root that produces\nthe value for this sublist column. Paths are\nalways anchored at the source record root —\nnever relative to a pre-iterated row. The\nplatform stores `null` here on fields driven by\n`hardCodedValue` instead of an extract path."},"generate":{"type":"string","description":"NetSuite sublist field ID to write to."},"hardCodedValue":{"type":"string","description":"Static value for this sublist field."},"lookupName":{"type":"string","description":"Reference to a lookup by name."},"dataType":{"type":"string","description":"Data type hint for the field value."},"internalId":{"type":"boolean","description":"When true, the value is a NetSuite internal ID reference."},"isKey":{"type":"boolean","description":"When true, this field is a key field for matching existing sublist lines."},"immutable":{"type":"boolean","description":"When true, this field is only set on record creation, not on updates."},"discardIfEmpty":{"type":"boolean","description":"When true, skips this field mapping if the extracted value is empty."},"extractDateFormat":{"type":"string","description":"Date format of the extracted value."},"extractDateTimezone":{"type":["string","null"],"description":"Timezone of the extracted date value. Stored as `null` when not configured."},"subRecordMapping":{"type":"object","description":"Nested Mapper 1.0 mapping for a NetSuite\nsubrecord embedded in this sublist-field.  A\nsubrecord is a child record that only exists\ninside a parent (e.g. the Inventory Detail on\na serialized-item line of a sales order);\nNetSuite reaches it through the parent.\n\n**Recursive structure.**  The nested\n`mapping` object has the same\n`{fields, lists}` shape as the top-level\n`netsuite_da.mapping`.  Recursion is allowed\nbut rare.","properties":{"recordType":{"type":"string","description":"NetSuite's internal record-type id of the\nSUBRECORD (not the parent).  E.g.\n`inventorydetail`, `itempricing`,\n`componentinventorydetail`.\n"},"jsonPath":{"type":"string","description":"JSONPath (relative to the current sublist\nline) to the rows that populate the\nsubrecord.  Typically `$` when the\nsubrecord data is carried on the same\nline item (the enclosing\n`lists[].jsonPath` has already selected\nthe line).  Defaults to `$`.\n"},"mapping":{"type":"object","description":"Nested `{fields, lists}` mapping for the\nsubrecord.  Identical shape to the\ntop-level `netsuite_da.mapping`.\nSubrecords often have empty `fields` and\nonly a sublist (e.g. `inventorydetail` →\n`inventoryassignment`).\n"},"lookups":{"type":"array","description":"Subrecord-scoped lookups.  Same shape as\nthe top-level `netsuite_da.lookups`.\nFields inside this\n`subRecordMapping.mapping` reference\nthem via `lookupName`.  Often `[]`.\n"},"referenceFieldId":{"type":"string","description":"Secondary reference-field identifier used\non a small fraction of subrecord blocks\nin production (~3%).  Exact semantics are\nnot publicly documented by Celigo — match\nthe value on existing resources when\npresent; do NOT synthesize one.  Leave\nunset unless a template clearly requires\nit.\n"}}},"conditional":{"type":"object","description":"Conditional logic for when to apply this sublist field mapping\n(\"Only perform mapping when\" in the UI). Lookup-based conditions\nevaluate the lookup named by `lookupName`, which must be defined\non this import.","properties":{"lookupName":{"type":"string","description":"Lookup to evaluate for the condition. Must exactly match the\n`name` of a lookup defined on this import."},"when":{"type":"string","description":"Condition that controls when this field mapping is applied.","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","ignore_if_set"]}}}}},"description":"Field mappings for each column in the sublist."}}},"description":"Sublist (line-item) mappings. Each entry maps source data to a NetSuite sublist.\n"}}},"lookups":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia `lookupName` and Handlebars expressions reference it via\n`{{lookup \"name\" value}}`. Must be unique within this\nresource's `lookups` array."},"recordType":{"type":"string","description":"NetSuite record type to search. Use the internal record-type\nid (lowercase, underscored), e.g. `customer`, `item`,\n`salesorder`, `customlist_xxx`."},"searchField":{"type":"string","description":"NetSuite field on `recordType` to match against. Use the\ninternal field id — e.g. `email`, `externalid`,\n`internalid`, `entityid`, `custitem_xxx`."},"resultField":{"type":"string","description":"NetSuite field whose value is returned when a record matches.\nTypically `internalid` when the lookup resolves to a record\nreference, or any queryable scalar field."},"expression":{"type":"string","description":"Optional advanced-search expression (NetSuite saved-search\nDSL) for multi-criterion lookups. When present, supersedes\n`searchField` / `operator` — those fields are ignored."},"operator":{"type":"string","description":"Comparison operator for the `searchField` match. Common\nvalues: `is`, `contains`, `startswith`, `anyof`,\n`equalto`, `greaterthan`."},"includeInactive":{"type":"boolean","description":"When true, includes inactive records in lookup results."},"useDefaultOnMultipleMatches":{"type":"boolean","description":"Controls behaviour when the search matches more than one\nrecord. When true, the `default` value is returned instead\nof raising an ambiguous-match error. When false (default),\nmultiple matches are treated as a failure."},"allowFailures":{"type":["boolean","null"],"description":"When true, a lookup miss (no matching record and no\n`default`) resolves to `null` and the import continues.\nWhen false (default), a miss halts the record. May be\nstored as null (treated as unset)."},"map":{"type":["object","null"],"description":"Optional static key→value object evaluated BEFORE the\nNetSuite search. When the incoming value matches a key, the\nmapped value is returned without hitting NetSuite. Null on\ndynamic (search-based) lookups with no static map."},"default":{"type":["string","null"],"description":"Value returned when the search matches no records. When\nomitted and `allowFailures` is false, an unmatched lookup\nhalts the record. May be stored as null (no default)."}}},"description":"Lookup definitions that resolve reference values from NetSuite.\nReferenced by name from field mappings via `lookupName` and from\nHandlebars expressions via `{{lookup \"name\" value}}`."},"rawOverride":{"type":"object","description":"Raw override object for advanced use cases. When useRawOverride is true,\nthis object is sent directly to the NetSuite API, bypassing normal mapping.\n"},"useRawOverride":{"type":"boolean","description":"When true, uses rawOverride instead of the normal mapping configuration.\n"},"isMigrated":{"type":"boolean","description":"When true, this import was migrated from a legacy format. Set by the system."},"retryUpdateAsAdd":{"type":"boolean","description":"When true, if an update fails because the record doesn't exist, automatically\nretry as an add operation. Useful for initial syncs where records may not exist yet.\n"},"customFieldMetadata":{"type":"object","description":"Metadata about custom fields on the target record type.\nPopulated by the system from NetSuite metadata — do not set manually.\n"},"file":{"type":"object","description":"File cabinet configuration for file-based imports into NetSuite.\n","properties":{"name":{"type":"string","description":"Filename for the file in NetSuite File Cabinet."},"fileType":{"type":"string","description":"NetSuite file type (e.g., \"PDF\", \"CSV\", \"PLAINTEXT\", \"EXCEL\", \"XML\").\n"},"folder":{"type":"string","description":"Folder path or name in the NetSuite File Cabinet."},"folderInternalId":{"type":"string","description":"Internal ID of the target folder in NetSuite File Cabinet."},"internalId":{"type":"string","description":"Internal ID of an existing file to update."},"backupFolderInternalId":{"type":"string","description":"Internal ID of a backup folder for file versioning."}}},"isFileProvider":{"type":"boolean","description":"Whether this import handles files in the NetSuite File Cabinet.\n"},"preferences":{"type":"object","properties":{"ignoreReadOnlyFields":{"type":"boolean","description":"When true, silently skip read-only fields instead of raising errors."},"warningAsError":{"type":"boolean","description":"When true, treat NetSuite warnings as errors that stop the import."},"skipCustomMetadataRequests":{"type":"boolean","description":"When true, skip fetching custom field metadata to improve performance."}},"description":"Import behavior preferences that control how NetSuite handles the import operation.\n"},"recordTypeId":{"type":"string","description":"The internal record type ID. Used for custom record types in NetSuite where\nthe numeric ID is needed in addition to the recordType string.\n"}},"if":{"not":{"propertyNames":{"enum":["lookups","mapping","missingOrCorruptedDAConfig"]}}},"then":{"required":["operation","recordType"],"if":{"properties":{"operation":{"enum":["update","addupdate","delete"]}},"required":["operation"]},"then":{"required":["internalIdLookup"]}}},"Rdbms":{"type":"object","description":"Configuration for RDBMS imports into SQL Server, MySQL, PostgreSQL, Snowflake, Oracle, MariaDB, and other relational databases.\nqueryType determines which companion field is required: per_record, per_page, and first_page use the query field; bulk_insert uses the bulkInsert object; bulk_load uses the bulkLoad object — never set query and bulkInsert together.\n(The server itself stores an empty `query: []` next to bulk_insert/bulk_load configs and may leave an empty `bulkInsert: {}` stub next to others — empty companions are normal in responses; only a populated conflicting companion is invalid.)\nThe query field is an array of plain SQL strings, not a single string and not an array of objects.","properties":{"lookups":{"$ref":"#/components/schemas/Lookups","description":"Lookup definitions executed against the RDBMS connection. Each\nentry runs a `SELECT` and extracts a value used by field mappings\n(referenced via `lookupName`) or Handlebars expressions\n(referenced via `{{lookup \"name\" value}}`).\n\n**Mirror invariant**\nThe Celigo platform persists lookups in TWO locations on an RDBMS\nimport: top-level `lookups` and `rdbms.lookups`. Both arrays\nmust hold byte-for-byte identical content — the MappingsAgent\nwrites both on every save.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia `lookupName` and Handlebars expressions reference it via\n`{{lookup \"name\" value}}`. Must be unique within this\nresource's `lookups` array.\n"},"query":{"type":"string","description":"SQL `SELECT` statement that resolves the lookup value.\nTypically returns a single row with a single column; use\n`extract` to pick the column when the query returns multiple.\nInject values from the incoming record with Handlebars — RDBMS\nqueries use triple braces and the `record.` prefix:\n`SELECT id FROM users WHERE email = '{{{record.email}}}'`.\n"},"extract":{"type":"string","description":"JSONPath-style expression that selects the value from the query\nresult row (e.g. `id`, `details.price`). Omit when the\nquery returns a single scalar — the first column is used\nautomatically.\n"},"_lookupCacheId":{"type":"string","format":"objectId","description":"Optional reference to a LookupCache resource. When set, the\nlookup reads from the cache instead of issuing a live SQL\nquery — useful for stable reference data that should be\npre-loaded.\n"},"map":{"type":"object","description":"Optional static key→value object evaluated BEFORE the SQL query.\nWhen the incoming value matches a key, the mapped value is\nreturned without querying the database. Useful for small\ncontrolled enumerations.\n"},"default":{"type":"string","description":"Value returned when the query matches no rows. When omitted and\n`allowFailures` is false, an unmatched lookup halts the\nrecord.\n"},"allowFailures":{"type":"boolean","description":"When true, a lookup miss (no matching row and no `default`)\nresolves to `null` and the import continues. When false\n(default), a miss halts the record.\n"}}},"type":"array"},"query":{"type":"array","items":{"type":"string"},"description":"SQL statements to execute; required when queryType is `[\"per_record\"]`, `[\"per_page\"]`, or `[\"first_page\"]`, and typically contains a single statement.\nEach element is a complete SQL statement as a plain string — never a single bare string and never an array of objects (objects cause a Cast error).\nInject values from the incoming record with Handlebars using the `record.` prefix and triple braces, e.g. `'{{{record.name}}}'` for strings (single-quoted in the SQL) and `{{{record.quantity}}}` for numbers.\nRendering is strict: a reference the render model cannot resolve (a bare `{{name}}` without prefix, or `record.*` when the arriving record is a grouped array) fails the step with `cannot_evaluate_handlebars` — it does not render an empty value.\n`record` and `rows` are aliases chosen by the arriving data's shape: an object record binds `record` (`rows` undefined); a grouped array-record (e.g. upstream `groupByFields`) binds `rows` (`record` undefined) — address grouped rows as `{{rows.0.field}}` or iterate `{{#each rows}}`.\nDouble braces (`{{record.field}}`) output the value wrapped in single quotes with embedded quotes doubled, while triple braces output it raw; block helpers such as `{{#each}}` and `{{#if}}` use double braces as normal."},"queryType":{"type":["array","null"],"items":{"type":"string","enum":["INSERT","UPDATE","bulk_insert","per_record","per_page","first_page","bulk_load"]},"description":"Execution strategy for the SQL operation, which determines the required companion field: per_record, per_page, and first_page require `query`; bulk_insert requires `bulkInsert`; bulk_load requires `bulkLoad`.\nPrefer the highest-performance type the operation supports: bulk_load (currently Snowflake and NSAW — handles INSERT, upsert via `bulkLoad.primaryKeys`, and custom merge or ignore-existing logic via `bulkLoad.overrideMergeQuery`), then bulk_insert for pure INSERTs, then per_page, then per_record only when the logic cannot be expressed as a batch operation.\nFor UPDATE, UPSERT, or ignore-existing logic on databases without bulk_load support, use per_page or per_record with `query` — bulk_insert has no duplicate-checking logic.\nSet a single value; do not combine types or use the legacy INSERT and UPDATE values.\nper_page statements render against the whole batch (`batch_of_records`) rather than a single record — loop with `{{#each batch_of_records}}...{{{record.fieldName}}}...{{/each}}`.\n\n`bulk_load` — Stages data as a file and loads via database-native COPY/bulk mechanism for maximum throughput; supports INSERT, upsert via bulkLoad.primaryKeys, and custom merge or ignore-existing logic via bulkLoad.overrideMergeQuery; currently supported for Snowflake and NSAW.\n`bulk_insert` — Batch INSERT via multi-row VALUES clause for efficient bulk data loading; has no upsert or ignore-existing logic and is available for all RDBMS types.\n`per_page` — Executes one SQL statement per page (batch) of records; available for all RDBMS types.\n`per_record` — Executes one SQL statement per record, giving full control over individual record SQL; available for all RDBMS types."},"bulkInsert":{"type":"object","description":"Configuration for batch INSERTs via a multi-row VALUES clause; required when queryType is `[\"bulk_insert\"]` and must not be set otherwise.\nOnly suited to pure INSERTs — for UPDATE, UPSERT, or ignore-existing/skip-duplicates logic, use queryType `[\"per_record\"]` with the `query` field instead.","properties":{"tableName":{"type":"string","description":"The name of the database table into which the bulk insert operation will be executed. This value must correspond to a valid, existing table within the target relational database management system (RDBMS). It serves as the primary destination for inserting multiple rows of data efficiently in a single operation. The table name can include schema or namespace qualifiers if supported by the database (e.g., \"schemaName.tableName\"), allowing precise targeting within complex database structures. Proper validation and sanitization of this value are essential to ensure the operation's success and to prevent SQL injection or other security vulnerabilities."},"batchSize":{"type":"string","description":"The number of records to be inserted into the database in a single batch during a bulk insert operation. This parameter is crucial for optimizing the performance and efficiency of bulk data loading by controlling how many records are grouped together before being sent to the database. Proper tuning of batchSize balances memory consumption, transaction overhead, and throughput, enabling the system to handle large volumes of data efficiently without overwhelming resources or causing timeouts. Adjusting batchSize directly impacts transaction size, network utilization, error handling granularity, and recovery strategies, making it essential to tailor this value based on the specific database capabilities, system resources, and workload characteristics."}}},"bulkLoad":{"type":"object","properties":{"tableName":{"type":"string","description":"Target table for the bulk load.\nMust reference an existing table accessible with the connection's credentials; schema-qualified names are supported."},"primaryKeys":{"type":"array","items":{"type":"string"},"description":"Columns that uniquely identify each record in the target table, used to match existing rows during the load.\nSet for upsert behavior (a MERGE is auto-generated); omit (or empty) for a pure INSERT.\nComposite keys are supported — list every key column, and names must exactly match the target schema."},"overrideMergeQuery":{"type":"boolean","description":"When true, a custom merge query replaces the auto-generated MERGE statement, enabling ignore-existing logic, conditional updates, or multi-table operations.\nThe override SQL references `{{import.rdbms.bulkLoad.preMergeTemporaryTable}}` for the staging table."}},"description":"Configuration for bulk loading: data is staged as a file and loaded via the database's native COPY/bulk mechanism for maximum throughput.\nRequired when queryType is `[\"bulk_load\"]`; currently supported for Snowflake and NSAW."},"updateLookupName":{"type":["string","null"],"description":"Name of the lookup used for update operations in the legacy composite (queryType1) flow."},"updateExtract":{"type":["string","null"],"description":"Path used to extract the value that drives update operations in the legacy composite flow."},"ignoreLookupName":{"type":["string","null"],"description":"Name of the lookup used to determine which records to ignore when ignore-existing is enabled."},"ignoreExtract":{"type":["string","null"],"description":"Path used to extract the value that determines whether a record is ignored when ignore-existing is enabled."}},"if":{"not":{"propertyNames":{"enum":["lookups"]}}},"then":{"required":["queryType"],"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"enum":["per_record","per_page"]}}}},"then":{"required":["query"],"properties":{"bulkInsert":{"maxProperties":0}}},"else":{"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"const":"first_page"}}}},"then":{"required":["query"]},"else":{"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"const":"bulk_insert"}}}},"then":{"required":["bulkInsert"],"properties":{"query":{"maxItems":0}}},"else":{"if":{"required":["queryType"],"properties":{"queryType":{"type":"array","contains":{"const":"bulk_load"}}}},"then":{"required":["bulkLoad"],"properties":{"query":{"maxItems":0}}}}}}}},"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 set to true\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\nIf allowFailures is false and no default is specified, the flow will fail\nwhen encountering unmapped values.\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\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\nThe best practice is typically to set allowFailures to true with a meaningful\ndefault value, so flows remain operational while alerting you to missing mappings.\n"}}}},"S3":{"type":"object","description":"Defines where files are written to 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 destination, fileKey sets the object key, and backupBucket\nretains a copy after a successful import.","required":["region","bucket"],"properties":{"region":{"type":"string","default":"us-east-1","description":"AWS region where the S3 bucket resides. Must match the bucket's actual location to avoid\nconnectivity errors and misrouted requests."},"bucket":{"type":"string","description":"S3 bucket that receives the imported files. The bucket must already exist and the\nconnection's AWS credentials must have s3:PutObject permission on it."},"fileKey":{"type":"string","description":"Object key under which each file is stored in the bucket. Include slashes to organize\nfiles in a folder-style hierarchy (for example, imports/2023/12/orders.json); keys are\ncase-sensitive. Supports handlebars placeholders such as orders-{{timestamp}}.json to\ngenerate a unique key per run."},"backupBucket":{"type":"string","description":"S3 bucket where a copy of each imported file is retained after a successful import; if\nomitted, no backup copy is kept. Must reference an existing bucket the connection's\ncredentials have s3:PutObject permission on."},"serverSideEncryptionType":{"type":"string","description":"Server-side encryption applied to uploaded objects. The connection form sets `AES256`\n(SSE-S3, S3-managed keys) when encryption is enabled; leave unset to use the bucket's\ndefault encryption."}}},"Wrapper":{"type":"object","description":"Configuration for Wrapper imports, which delegate writing records 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.","properties":{"function":{"type":"string","description":"Name of the function the wrapper invokes to process records.\nMust match a callable function in the wrapper's execution context; names are case-sensitive."},"configuration":{"type":"object","additionalProperties":true,"description":"Free-form settings passed to the wrapper function at runtime (connector-specific keys such\nas method, apiVersion, headers, or handler). Structure is defined by the wrapper code, not\nby this schema."},"lookups":{"$ref":"#/components/schemas/Lookups","description":"Lookup definitions for this wrapper import. Wrapper lookups use\nthe generic Celigo shape: either a static `map` (key→value\nobject) or a dynamic `_lookupCacheId` + `extract` pair. See\n`common/schemas/lookups.yml` for the full item shape."}},"if":{"not":{"propertyNames":{"enum":["lookups"]}}},"then":{"required":["function"]}},"Salesforce":{"type":"object","description":"Salesforce-specific configuration for the import: the operation to perform, the API to use, and object-level settings such as `sObjectType`, `idLookup`, and `upsert`. Properties like `ignoreExisting`, `ignoreMissing`, `name`, and `description` are not part of this object — they belong at the import level.","properties":{"lookups":{"description":"Lookup definitions used to resolve Salesforce record references or\nstatic value mappings. Each lookup has a unique ``name`` that mapping\nfields reference via ``lookupName`` and that Handlebars expressions\nreference via ``{{lookup \"name\" value}}``.\n\n**Mirror invariant**\nThe Celigo platform stores lookups in TWO locations on a Salesforce\nimport resource: top-level ``lookups`` and ``salesforce.lookups``.\nBoth arrays must hold byte-for-byte identical content — the\nMappingsAgent writes both on every save.\n\n**Static vs dynamic lookups**\n- **Static** — only ``map`` + optional ``default``. No Salesforce\n  query runs. Use for small controlled vocabularies (country codes,\n  status labels, etc.).\n- **Dynamic** — ``sObjectType`` + ``resultField`` + ``whereClause``.\n  Issues a SOQL query at runtime. Use ``map`` alongside the query to\n  short-circuit known values before hitting Salesforce.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup entry. Mapping fields reference\nit via ``lookupName``, and Handlebars expressions reference it\nvia ``{{lookup \"name\" value}}``. Must be unique within this\nresource's ``lookups`` array. Conventionally a short slug or a\ngenerated hash — the value is only meaningful as a reference\ntoken.\n"},"sObjectType":{"type":"string","description":"Salesforce sObject API name to query (e.g. ``Account``,\n``Contact``, ``Solution__c``). For custom objects include the\n``__c`` suffix. Case-sensitive — must match exactly as declared\nin the target Salesforce org.\n"},"resultField":{"type":"string","description":"sObject field whose value is returned when the SOQL ``whereClause``\nmatches a record. Typically ``Id`` when the lookup resolves to a\nSalesforce record reference, but any queryable field is valid.\n"},"whereClause":{"type":"string","description":"SOQL ``WHERE`` clause that identifies the matching Salesforce\nrecord. Use Handlebars expressions (triple braces recommended:\n``{{{field}}}``) to inject values from the incoming source record.\nWrap the whole expression in parentheses.\n\nExamples:\n- ``(Id = {{{id [Customer SFDC ID]}}})``\n- ``(Email = '{{{email}}}')``\n- ``(External_Id__c = {{{string externalId}}} AND IsActive = true)``\n\nCeligo's Handlebars helpers (``string``, ``id``, ``double``,\n``bool``, ``date``) produce correctly-typed SOQL literals —\nprefer them over manual quoting. String values wrap in single\nquotes; numeric/id values do not.\n"},"map":{"type":["object","null"],"description":"Optional static key→value map evaluated BEFORE the SOQL query.\nWhen the incoming field value matches a key, the mapped value\nis returned without hitting Salesforce. Useful for small\ncontrolled enumerations where a full query would be overkill.\n"},"default":{"type":["string","null"],"description":"Value returned when the SOQL query matches no records. When\nomitted and ``allowFailures`` is false, an unmatched lookup\nfails the record.\n"},"allowFailures":{"type":"boolean","description":"When true, a lookup miss (no matching record and no ``default``)\nis tolerated — the mapped field resolves to ``null`` and the\nimport continues. When false (default), a miss halts processing\nfor the record.\n"},"useDefaultOnMultipleMatches":{"type":"boolean","description":"Controls behaviour when the SOQL query matches more than one\nrecord. When true, the ``default`` value is returned instead of\nraising an ambiguous-match error. When false (default), multiple\nmatches are treated as a failure.\n"}}},"type":"array"},"operation":{"type":"string","enum":["insert","update","upsert","upsertpicklistvalues","delete","addupdate"],"description":"Controls how records are written to Salesforce. Each value pairs with a lookup strategy: `upsert` requires `upsert.externalIdField` plus `idLookup.extract`, while `update`, `delete`, `addupdate`, and `insert` with `ignoreExisting` require `idLookup.whereClause`. Values are converted to lowercase automatically."},"api":{"type":"string","enum":["soap","rest","metadata","compositerecord"],"description":"Selects the Salesforce API used to execute the import. Use `soap` for most imports and another API only when its specific capability is needed; default to `soap` when no API is specified. Values are converted to lowercase automatically."},"soap":{"type":"object","properties":{"headers":{"type":"object","properties":{"allOrNone":{"type":"boolean","description":"When true, the batch is processed atomically: if any record fails, the entire transaction is rolled back and nothing is committed. When false or omitted, records that succeed are committed even if others in the batch fail."}},"description":"SOAP headers included in requests sent to Salesforce, such as the `allOrNone` transaction-control header."},"batchSize":{"type":"number","description":"Number of records sent to Salesforce in each SOAP API batch. Larger batches reduce the number of API calls; smaller batches reduce per-call load and the chance of timeouts."}},"description":"Settings that apply when `api` is `soap`, including SOAP request headers and batch size."},"sObjectType":{"type":"string","description":"Salesforce object the import writes to, such as `Account`, `Contact`, or a custom object like `Vendor__c`. Must exactly match the object's case-sensitive API name; custom objects include the `__c` suffix."},"idLookup":{"type":"object","properties":{"extract":{"type":"string","description":"Field in the incoming data whose value is matched against the Salesforce External ID field named in `upsert.externalIdField`. Set only when `operation` is `upsert`; all other operations use `whereClause` instead. Choose a field that is unique and always populated — missing or null values cause the upsert to fail. If both `extract` and `whereClause` are set, `extract` takes precedence for upsert."},"whereClause":{"type":"string","description":"SOQL condition (without the `WHERE` keyword) that finds existing Salesforce records for each incoming record. Required when `operation` is `update`, `delete`, or `addupdate`, and for `insert` with `ignoreExisting`; for `upsert`, use `extract` instead. Reference incoming fields with triple-brace Handlebars and wrap string values in single quotes (e.g. `Email = '{{{Email}}}'`) — triple braces emit the raw value without HTML-escaping, which would corrupt characters like `&` or `'`. Combine conditions with `AND`/`OR`."}},"description":"Controls how existing Salesforce records are matched before writing. Set exactly one of `extract` (for `upsert`) or `whereClause` (for `update`, `delete`, `addupdate`, and `insert` with `ignoreExisting`); omit for plain inserts.\n\nRequired when operation is addupdate, delete, update, or upsert."},"upsert":{"type":"object","properties":{"externalIdField":{"type":"string","description":"API name of the Salesforce field, marked as External ID, that upsert operations match against. This is the target side of the match; `idLookup.extract` names the source field in the incoming data. If the value matches multiple Salesforce records, the upsert fails."}},"description":"Upsert matching configuration, required when `operation` is `upsert` and omitted for all other operations. Contains only `externalIdField`, which pairs with `idLookup.extract` at the parent level.\n\nRequired when operation is upsert."},"upsertpicklistvalues":{"type":"object","properties":{"type":{"type":"string","enum":["picklist","multipicklist"],"description":"Picklist field type being managed. Values are converted to lowercase automatically."},"fullName":{"type":"string","description":"Fully qualified API name of the picklist field, in `ObjectName.FieldName__c` form (for example, `Account.MyPicklist__c`). Must match the case-sensitive Salesforce API name."},"label":{"type":"string","description":"Display label for the picklist field in the Salesforce UI. Can contain spaces and does not need to match the API name."},"visibleLines":{"type":"number","description":"Number of values visible without scrolling in the Salesforce UI selection box. Only applies when `type` is `multipicklist`."}},"description":"Picklist field definition managed when `operation` is `upsertpicklistvalues`; omit for all other operations. Manages the field's metadata — name, label, and display settings — and requires Salesforce Metadata API permissions.\n\nRequired when operation is upsertpicklistvalues."},"removeNonSubmittableFields":{"type":"boolean","description":"When true, strips fields Salesforce will not accept — read-only, system-generated, and formula fields — from the payload before submission, preventing rejection errors. When false or omitted, all fields are submitted as-is. Only the outgoing payload is affected; the source data is unchanged."},"document":{"type":"object","properties":{"id":{"type":"string","description":"Salesforce-assigned ID of the Document record. The 15-character form is case-sensitive; the 18-character form adds a case-insensitivity checksum."},"name":{"type":"string","description":"Display name of the document in Salesforce, used to identify it in folders, search, and API operations."},"folderId":{"type":"string","description":"Salesforce folder that stores the document. The document inherits the folder's sharing and visibility settings; changing this value moves the document to a different folder."},"contentType":{"type":"string","description":"MIME type of the document content, such as `application/pdf` or `image/png`."},"developerName":{"type":"string","description":"Unique API name used to reference the document in code, metadata, and integrations. Distinct from the display name; uses only letters, numbers, and underscores with no spaces."},"isInternalUseOnly":{"type":"boolean","description":"When true, marks the document for internal use only — it must not be shared with parties outside the organization."},"isPublic":{"type":"boolean","description":"When true, the document is accessible to all users; when false, access is limited to authorized users by Salesforce permissions and sharing settings."}},"description":"Salesforce Document record written by the import, combining the file content with metadata such as name, folder, and content type."},"attachment":{"type":"object","properties":{"id":{"type":"string","description":"Salesforce-assigned ID of the Attachment record. Prefer the 18-character case-insensitive form over the 15-character case-sensitive form in integrations."},"name":{"type":"string","description":"Filename of the attachment, including the file extension (for example, `contract_agreement.pdf`)."},"parentId":{"type":"string","description":"Salesforce record the attachment is linked to, such as an Account, Contact, or Opportunity. Required when creating an attachment; access to the attachment follows the parent record's permissions."},"contentType":{"type":"string","description":"MIME type of the attachment content, such as `application/pdf` or `image/png`."},"isPrivate":{"type":"boolean","description":"When true, the attachment is visible only to users explicitly authorized to access it; when false or omitted, it inherits the visibility of its parent record. This setting only restricts access further — it never grants access beyond the parent record's permissions."},"description":{"type":"string","description":"Free-text note describing the attachment's content or purpose, shown alongside the attachment details in Salesforce."}},"description":"Salesforce Attachment record written by the import — a file linked to a parent record via `parentId`, with metadata such as name and content type."},"contentVersion":{"type":"object","properties":{"contentDocumentId":{"type":"string","description":"ContentDocument this version belongs to; all versions of the same document share this ID. Assigned by Salesforce when the content version is created and immutable afterward."},"title":{"type":"string","description":"Display title of this content version, shown in content libraries, search results, and version history."},"pathOnClient":{"type":"string","description":"Original file path on the client machine before upload. Used for reference and display only; it does not affect how the file is stored or accessed in Salesforce."},"tagCsv":{"type":"string","description":"Comma-separated tags applied to the content version for categorization and search. Updating this field replaces the entire tag set, so include every tag the version should keep."},"contentLocation":{"type":"string","description":"Specifies where the content file for this ContentVersion record is stored — \"S\" for Salesforce internal storage or \"E\" for an external system (the two values the import form offers). Salesforce also defines \"L\" for content shared via a link, but the form does not set it. Typically assigned automatically based on the upload method."}},"description":"Salesforce ContentVersion record written by the import — a specific version of a content item in Salesforce Files. Each modification produces a new version, and all versions of a document are linked through `contentDocumentId`."}},"if":{"anyOf":[{"required":["attachment"]},{"required":["contentVersion"]}]},"then":{"required":["operation"]},"else":{"if":{"not":{"propertyNames":{"enum":["lookups","api"]}}},"then":{"required":["operation","api"],"if":{"properties":{"operation":{"const":"upsert"}},"required":["operation"]},"then":{"required":["idLookup","upsert"],"properties":{"idLookup":{"required":["extract"]},"upsert":{"required":["externalIdField"]}}},"else":{"if":{"properties":{"operation":{"enum":["update","delete","addupdate"]}},"required":["operation"]},"then":{"required":["idLookup"],"properties":{"idLookup":{"required":["whereClause"]}}},"else":{"if":{"properties":{"operation":{"const":"upsertpicklistvalues"}},"required":["operation"]},"then":{"required":["upsertpicklistvalues"],"properties":{"upsertpicklistvalues":{"required":["fullName"]}}}}}}}},"Tool":{"type":"object","description":"Configuration for a `ToolImport`, which executes a reusable Celigo Tool resource as the\nimport action. The referenced tool defines its own input contract, processing pipeline, and\noutput; this object binds the tool to the import and optionally overrides the connections the\ntool uses for this invocation.","required":["_toolId"],"properties":{"_toolId":{"type":"string","format":"objectId","description":"Tool resource this import executes. The tool supplies the input schema, routing, page\nprocessors, and output mapping; the import runs that tool when records reach it. Required —\na `ToolImport` has no behavior without a tool to run."},"overrides":{"type":"object","description":"Per-import overrides applied to the referenced tool at execution time. Tools use a\nbring-your-own-keys connection model: the tool declares abstract connection slots, and the\nconsumer binds concrete connections here so one tool definition can run against different\nsystems without being modified. Omit to run the tool with its default connections.","properties":{"connections":{"type":"array","description":"Connection bindings that replace the tool's abstract connection slots for this import.\nEach entry maps one of the tool's abstract connections to the concrete connection the\nimport should use.","items":{"type":"object","required":["_id","_abstractId"],"properties":{"_id":{"type":"string","format":"objectId","description":"Concrete connection bound to the abstract slot for this import's tool run."},"_abstractId":{"type":"string","format":"objectId","description":"Abstract connection slot, declared by the tool, that this binding replaces. Matches\nthe abstract connection id defined on the referenced tool."},"_borrowConcurrencyFromConnectionId":{"type":"string","format":"objectId","description":"Optional pointer to another connection whose\nconcurrency budget this binding shares. Use\nwhen several wrappers / steps should share rate\nlimits against the same downstream system but\nuse different credentials. Mutually exclusive\nwith `_id` on the same entry.\n"}}}}}}}},"File":{"type":"object","description":"**CRITICAL: This object is REQUIRED for all file-based import adaptor types.**\n\n**When to include this object**\n\n✅ **MUST SET** when `adaptorType` is one of:\n- `S3Import`\n- `FTPImport`\n- `AS2Import`\n- `HTTPImport` in file mode (`http.type: \"file\"` — the folder mode of the cloud file\n  providers such as Google Drive, Box, Dropbox, Azure Blob, Google Cloud Storage and Celigo\n  Storage); the same `file` object, including `type: filedefinition` with `_ediProfileId`\n  for EDI, applies as on S3 / FTP\n\n❌ **DO NOT SET** for non-file-based imports like:\n- `SalesforceImport`\n- `NetSuiteImport`\n- `HTTPImport` in records mode (`http.type` unset — a REST endpoint receiving records)\n- `MongodbImport`\n- `RDBMSImport`\n\n**Minimum required fields**\n\nFor most file imports, you need at minimum:\n- `fileName`: The output file name (supports Handlebars like `{{timestamp}}`)\n- `type`: The file format (json, csv, xml, xlsx)\n- `aggregation.type`: How the run's records are batched into files (`all` for one file per run)\n\n**Example (S3 Import)**\n\n```json\n{\n  \"file\": {\n    \"fileName\": \"customers-{{timestamp}}.json\",\n    \"aggregation\": { \"type\": \"all\" },\n    \"type\": \"json\"\n  },\n  \"s3\": {\n    \"region\": \"us-east-1\",\n    \"bucket\": \"my-bucket\",\n    \"fileKey\": \"customers-{{timestamp}}.json\"\n  },\n  \"adaptorType\": \"S3Import\"\n}\n```","properties":{"fileName":{"type":"string","description":"**REQUIRED for file-based imports.**\n\nThe name of the file to be created/written. Supports Handlebars expressions for dynamic naming.\n\n**Default behavior**\n- When the user does not specify a file naming convention, **always default to timestamped filenames** using `{{timestamp}}` (e.g., `\"items-{{timestamp}}.csv\"`). This ensures each run produces a unique file and avoids overwriting previous exports.\n- Only use a fixed filename (without timestamp) if the user explicitly requests overwriting or a fixed name.\n\n**Common patterns**\n- `\"data-{{timestamp}}.json\"` - Timestamped JSON file (DEFAULT — use this pattern when not specified)\n- `\"export-{{date}}.csv\"` - Date-stamped CSV file\n- `\"{{recordType}}-backup.xml\"` - Dynamic record type naming\n\n**Examples**\n- `\"customers-{{timestamp}}.json\"`\n- `\"orders-export.csv\"`\n- `\"inventory-{{date}}.xlsx\"`\n\n**Important**\n- For S3 imports, this should typically match the `s3.fileKey` value\n- For FTP imports, this should typically match the `ftp.fileName` value"},"skipAggregation":{"type":"boolean","description":"Legacy companion of `aggregation.type`: `true` when the run's records are written one\nfile per page, `false` when they are combined across the run or grouped by key.\nValidation reads the value sent — an EDI file definition needs `true` unless\n`aggregation.type` is `key`, and `batchSize` needs `true` — then the save rewrites it\nfrom `aggregation.type`, so send the two as a consistent pair.","default":false},"type":{"type":"string","enum":["json","csv","xml","xlsx","filedefinition"],"description":"**REQUIRED for file-based imports.**\n\nThe format of the output file.\n\nSelect the format that matches the target system's requirements."},"encoding":{"type":"string","enum":["utf8","win1252","utf-16le","gb18030","macroman","iso88591","shiftjis"],"description":"Character encoding for the output file.\n\n**Default:** `\"utf8\"`\n\nChange from the default only when the target system requires a specific encoding.","default":"utf8"},"delete":{"type":"boolean","description":"Whether to delete the source file after successful import.\n\n**Values**\n- `true`: Delete source file after processing\n- `false`: Keep source file\n\n**Default:** `false`","default":false},"compressionFormat":{"type":"string","enum":["gzip","zip"],"description":"Compression format for the output file.\n\n**When to use**\n- Large files that benefit from compression\n- When target system expects compressed files"},"backupPath":{"type":"string","description":"Path where backup copies of files should be stored.\n\n**Examples**\n- `\"backup/\"` - Relative backup folder\n- `\"/archive/2024/\"` - Absolute backup path"},"purgeInternalBackup":{"type":"boolean","description":"Whether to purge internal backup copies after successful processing.\n\n**Default:** `false`","default":false},"batchSize":{"type":"integer","description":"Number of records to include per batch/file when processing large datasets.\n\n**When to use**\n- Large imports that need to be split into multiple files\n- When target system has file size limitations\n\n**Note**\n- Offered when records are written one file per page (`aggregation.type: page` with\n  `skipAggregation: true`)"},"encrypt":{"type":"boolean","description":"Whether to encrypt the output file.\n\n**Values**\n- `true`: Encrypt the file (requires PGP configuration)\n- `false`: No encryption\n\n**Default:** `false`","default":false},"csv":{"type":"object","description":"CSV-specific configuration. Only used when `type` is `\"csv\"`.","properties":{"rowDelimiter":{"type":"string","description":"Character(s) used to separate rows. Default is newline.","default":"\n"},"columnDelimiter":{"type":"string","description":"Character(s) used to separate columns. Default is comma.","default":","},"includeHeader":{"type":"boolean","description":"When true, includes a header row with column names in the output.","default":true},"wrapWithQuotes":{"type":"boolean","description":"When true, wraps field values in quotes.","default":false},"replaceTabWithSpace":{"type":"boolean","description":"Replace tab characters with spaces.","default":false},"replaceNewlineWithSpace":{"type":"boolean","description":"Replace newline characters with spaces within fields.","default":false},"truncateLastRowDelimiter":{"type":"boolean","description":"Remove trailing row delimiter from the file.","default":false}}},"json":{"type":"object","description":"JSON-specific configuration. Only used when `type` is `\"json\"`.","properties":{"resourcePath":{"type":"string","description":"JSONPath expression to locate records within the JSON structure."}}},"xml":{"type":"object","description":"XML-specific configuration. Only used when `type` is `\"xml\"`.","properties":{"resourcePath":{"type":"string","description":"XPath expression to locate records within the XML structure."}}},"fileDefinition":{"type":"object","description":"Configuration settings for parsing files using a predefined file definition. This object enables processing of complex, non-standard, or proprietary file formats that require specialized parsing logic beyond what the standard parsers (CSV, JSON, XML, etc.) can handle.\n\n**When to use**\n\nConfigure this object when the `type` field is set to \"filedefinition\". This approach is required for properly handling:\n- Legacy or proprietary file formats with complex structures\n- Fixed-width text files where field positions are defined by character positions\n- Electronic Data Interchange (EDI) documents (X12, EDIFACT, etc.)\n- Multi-record type files where different lines have different formats\n- Files requiring complex preprocessing or custom parsing logic\n\n**File definition characteristics**\n\n- **Custom Parsing Rules**: Applies predefined parsing logic to complex file formats\n- **Reusable Configurations**: References externally defined parsing rules that can be reused\n- **Complex Format Support**: Handles formats that standard parsers cannot process\n- **Specialized Processing**: Often used for industry-specific or legacy formats\n\n**Implementation strategy for ai agents**\n\n1. **Format Analysis**:\n    - Determine if the file format is standard (CSV, JSON, XML) or requires custom parsing\n    - Check if the format follows industry standards like EDI, SWIFT, or fixed-width\n    - Assess if there are multiple record types within the same file\n    - Identify if specialized logic is needed to interpret the file structure\n\n2. **File Definition Selection**:\n    - Verify that a suitable file definition has already been created in the system\n    - Check if existing file definitions match the format requirements\n    - Confirm the file definition ID from system administrators if needed\n    - Ensure the file definition is compatible with the export's needs\n","properties":{"_fileDefinitionId":{"type":"string","format":"objectId","description":"Reference to the file definition resource."}}},"aggregation":{"type":"object","description":"Controls how a flow run's records are batched into output files — the transfer form's\n\"How many files would you like to generate?\" setting. The legacy `skipAggregation` flag\nmirrors `type` and is maintained by the platform.","properties":{"type":{"type":"string","enum":["all","page","key"],"description":"Which batches of a run's records become separate output files. EDI file definitions\n(`file.type: filedefinition`) are written per page or per key, never per run; `key`\nis accepted only for them and only when the account has B2B Manager. The grouping\nkey and the line-item loop it merges come from the EDI file definition's document\ntype, so there is no separate key field to configure."}}},"lookups":{"type":"array","description":"Named value-substitution maps available to this import's field mappings; each lookup\ntranslates source values to target values, with an optional default for unmatched values.\nReferenced by name from mapping rules.","items":{"type":"object","properties":{"name":{"type":"string","description":"Identifier used to reference this lookup from mapping rules."},"map":{"type":"object","description":"Source-value to target-value pairs applied during mapping.","additionalProperties":{"type":"string"}},"default":{"type":["string","null"],"description":"Value substituted when a source value is not found in map; empty string when omitted. May be stored as null (no default)."},"allowFailures":{"type":"boolean","description":"When true, a value missing from map does not fail the record and the default (or original value) is used."}}}},"pgp":{"type":"object","description":"PGP encryption settings applied when encrypt is true; selects the symmetric cipher and the\nsigning hash. The connection must hold the recipient's public key.","properties":{"symmetricKeyAlgorithm":{"type":"string","enum":["twofish","cast5","3des","aes128","aes192","aes256"],"default":"aes256","description":"Symmetric cipher used to encrypt the file body; aes256 unless the recipient requires another."},"hashAlgorithm":{"type":"string","enum":["sha256","sha384","sha512","sha224"],"description":"Hash algorithm used when signing the encrypted file; set only when the recipient requires a specific one."}}},"skipRename":{"type":"boolean","description":"When true, the import writes directly to the final file name instead of writing to a\ntemporary name and renaming on completion. Use only when the destination does not support\natomic rename; the default (false) uses the safe write-then-rename behavior."},"directory":{"type":"object","required":["pathMode"],"description":"Structured destination location for cloud file-provider imports (Google Drive shared\ndrives, Box, Dropbox), replacing the flat path used by classic FTP/S3 destinations. Select\nthe location by folder ID or by a path relative to a configured storage root via pathMode.\nFor these cloud providers, directoryId mode addresses the folder by id alone — the flat\ndestination-path field is not consulted at runtime.","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 destination directory (e.g. a Google Drive folder ID);\nset when pathMode is directoryId. This is the file provider's own ID, not a Celigo resource ID."},"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."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root the relative path resolves against; set when\npathMode is relativePath. The provider's own ID, not a Celigo resource ID."},"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 location for backup copies on cloud file providers — the file-provider\ncounterpart 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."}}}}},"FileSystem":{"type":"object","description":"Defines where files are written to 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 where generated files are written; the agent's OS\naccount must have write permission on it. Accepts a local OS path or a UNC network share,\nand supports handlebars templates for dynamic folders."}}},"AiAgentConfig":{"type":"object","description":"AI Agent configuration used by both AiAgentImport and GuardrailImport (ai_agent type).\n\nConfigures which AI provider and model to use, along with instructions, parameter\ntuning, output format, and available tools. Providers come in two families with\ntwo configuration formats:\n\n- **Built-in providers** — each has its own configuration block.\n  - **openai**: OpenAI models (GPT-4.1, GPT-5, etc.). Configure via the `openai` object.\n  - **gemini**: Google Gemini models via the LiteLLM proxy. Configure via `litellm` with overrides in `litellm._overrides.gemini`.\n  - **anthropic**: Anthropic Claude models via the LiteLLM proxy. Configure via `litellm` with overrides in `litellm._overrides.anthropic`.\n- **Catalog providers** — `mistral`, `xai`, `huggingface`, `deepseek`, `cohere`, `groq`. Added\n  as catalog data rather than platform code, they share one generic flat format:\n  `model`, `modelOptions`, `instructions`, `output`, and the `tools` / `prompts` /\n  `resources` arrays directly on this object.\n\nA `_connectionId` on the parent import selects bring-your-own-key (BYOK)\ncredentials. Without one, built-in providers run on platform-managed\ncredentials; catalog providers have no platform-managed credentials, so they\nsave without a connection but cannot run until one is attached.\n","required":["provider"],"properties":{"provider":{"type":"string","enum":["openai","gemini","anthropic","mistral","xai","huggingface","deepseek","cohere","groq"],"description":"AI provider to use."},"model":{"type":"string","description":"Model identifier for a catalog provider (generic flat format) — for example\n`grok-4.6`. Built-in providers set the model inside their own block\n(`openai.model`, `litellm.model`) instead."},"modelOptions":{"type":"object","description":"Model tuning for a catalog provider (generic flat format). Which keys, values, and\nranges are accepted is defined per catalog model and validated on save when the\nmodel is a catalog entry.","properties":{"maxOutputTokens":{"type":"number","description":"Maximum number of tokens the model may generate."},"temperature":{"type":"number","description":"Sampling temperature."},"topP":{"type":"number","description":"Nucleus sampling threshold."},"reasoning_effort":{"type":"string","enum":["low","medium","high","xhigh"],"description":"Reasoning depth for models that expose it."}}},"instructions":{"type":"string","description":"System prompt for a catalog provider (generic flat format). Built-in providers\nset it inside their own block (`openai.instructions`,\n`litellm._overrides.anthropic.systemInstruction`)."},"output":{"type":"object","description":"Output format for a catalog provider (generic flat format). Built-in providers\nconfigure it inside their own block (`openai.output`, `litellm.responseFormat`).","properties":{"format":{"type":"object","description":"Controls the structure of the model's output.","properties":{"type":{"type":"string","enum":["text","json_schema"],"description":"Output type."},"name":{"type":"string","description":"Name of the JSON schema, for `json_schema` output."},"strict":{"type":"boolean","description":"When true, the model must conform exactly to `jsonSchema`."},"jsonSchema":{"type":"object","additionalProperties":true,"description":"JSON Schema the structured output must conform to, for `json_schema` output."}}},"verbose":{"type":"string","description":"Level of detail in the model's response, for models that expose it."}}},"tools":{"type":"array","description":"Tools available to a catalog-provider agent (generic flat format). Entries mirror\nthe built-in providers' tool entries: a Celigo Tool by `_toolId`, an MCP server by\n`_mcpConnectionId`, or a vendor web-search tool.","items":{"type":"object","properties":{"type":{"type":"string","enum":["tool","mcp","web_search"],"description":"Type of tool entry."},"tool":{"type":"object","description":"Reference to a Celigo Tool resource, used when type is \"tool\".","properties":{"_toolId":{"type":"string","format":"objectId","description":"The Celigo Tool to call."}}},"mcp":{"type":"object","description":"MCP server connection, used when type is \"mcp\".","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if omitted). Each entry\nis either a plain tool name or an object carrying display metadata.","items":{"type":["string","object"]}}}},"config":{"type":"object","additionalProperties":true,"description":"Tool-specific options for vendor-native tools (for example web-search settings)."}}}},"prompts":{"type":"array","description":"MCP prompt entries available to a catalog-provider agent (generic flat format).\nEach entry references one MCP connection and the prompt names allowed from it.","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server that exposes the prompts."},"allowedPrompts":{"type":"array","description":"Prompt names the agent may fetch from the server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"},"openai":{"type":"object","description":"OpenAI-specific configuration. Used when `provider` is \"openai\".\n","required":["model","instructions"],"properties":{"instructions":{"type":"string","maxLength":1000000,"description":"System prompt that defines the AI agent's behavior, goals, and constraints.\n"},"model":{"type":"string","description":"OpenAI model identifier. Open string (not an enum) — model names change frequently."},"reasoning":{"type":"object","description":"Controls depth of reasoning for complex tasks.","properties":{"effort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh"],"description":"How much reasoning effort the model should invest"},"summary":{"type":"string","enum":["concise","auto","detailed"],"description":"Level of detail in reasoning summaries"}}},"temperature":{"type":"number","minimum":0,"maximum":2,"description":"Sampling temperature. Higher values (e.g. 1.5) produce more creative output,\nlower values (e.g. 0.2) produce more focused and deterministic output.\n"},"topP":{"type":"number","minimum":0.1,"maximum":1,"description":"Nucleus sampling parameter"},"topLogprobs":{"type":"number","minimum":0,"maximum":20,"description":"Number of most likely tokens to return log probabilities for at each output position."},"maxOutputTokens":{"type":"number","minimum":100,"maximum":128000,"default":5000,"description":"Maximum number of tokens in the model's response (server default observed live on create)"},"serviceTier":{"type":"string","enum":["auto","default","priority"],"default":"default","description":"OpenAI service tier. \"priority\" provides higher rate limits and\nlower latency at increased cost."},"output":{"type":"object","description":"Output format configuration","properties":{"format":{"type":"object","description":"Controls the structure of the model's output.\n","properties":{"type":{"type":"string","enum":["text","json_schema","blob"],"default":"text","description":"Output format type."},"schemaMode":{"type":"string","enum":["manual","json"],"description":"How the structured-output schema was authored in the UI.\nEditor state only — it does not change how `jsonSchema` is\nsent to the provider."},"name":{"type":"string","description":"Name for the output format (used with json_schema)"},"strict":{"type":"boolean","default":false,"description":"When true, enforces strict schema validation on output."},"jsonSchema":{"type":"object","description":"JSON Schema for structured output. Required when `format.type` is \"json_schema\".\n","properties":{"type":{"type":"string","description":"Root JSON Schema type of the structured output; use \"object\" for record-shaped results.","enum":["object","array","string","number","integer","boolean"]},"properties":{"type":"object","additionalProperties":true,"description":"JSON Schema definitions for each field the structured output may contain."},"required":{"type":"array","description":"Property names the model must include in the structured output.","items":{"type":"string"}},"additionalproperties":{"type":"boolean","description":"When true, the structured output may include properties beyond those defined in `properties`."}}}},"if":{"properties":{"type":{"const":"json_schema"}},"required":["type"]},"then":{"required":["name","jsonSchema"]}},"verbose":{"type":"string","enum":["low","medium","high"],"default":"medium","description":"Level of detail in the model's response"}}},"tools":{"type":"array","description":"Tools available to the AI agent during processing.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["web_search","mcp","image_generation","tool"],"description":"Type of tool."},"webSearch":{"type":"object","description":"Web search configuration (empty object to enable)"},"imageGeneration":{"type":"object","description":"Image generation configuration","properties":{"background":{"type":"string","description":"Controls whether generated images have a transparent or opaque background; use transparent only with output formats that support it (png, webp).","enum":["transparent","opaque"]},"quality":{"type":"string","description":"Rendering quality of generated images, trading detail for generation speed and file size.","enum":["low","medium","high"]},"size":{"type":"string","description":"Pixel dimensions of generated images; choose square, portrait, or landscape to match the intended use.","enum":["1024x1024","1024x1536","1536x1024"]},"outputFormat":{"type":"string","description":"File format of generated images; use png or webp when transparency is needed.","enum":["png","webp","jpeg"]}}},"mcp":{"type":"object","description":"MCP server tool configuration","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server"},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if\nomitted). Each entry is either a plain tool name (legacy\nform) or an object carrying display metadata.","items":{"type":["string","object"],"properties":{"name":{"type":"string","maxLength":256,"description":"Tool name as exposed by the MCP server."},"title":{"type":"string","maxLength":300,"description":"Display title shown for the tool."},"description":{"type":"string","maxLength":1000,"description":"Display description shown for the tool."}},"required":["name"]}},"allowedPrompts":{"type":"array","description":"Specific prompts to allow from the MCP server (used for MCP prompt entries; all if omitted).","items":{"type":"string"}}}},"tool":{"type":"object","description":"Reference to a Celigo Tool resource.\n","properties":{"_toolId":{"type":"string","format":"objectId","description":"Reference to the Tool resource"},"overrides":{"type":"object","description":"Per-agent overrides for the tool's internal resources","properties":{"connections":{"type":"array","description":"Remaps the tool's abstract connections for this agent. Each entry pairs\nthe tool's abstract connection placeholder (`_abstractId`) with the\nconcrete connection (`_id`) to use for this agent; entries without\n`_id` keep the tool's own default connection.\n","items":{"type":["object","null"],"required":["_abstractId"],"properties":{"_abstractId":{"type":"string","format":"objectId","description":"The tool's abstract connection placeholder being overridden."},"_id":{"type":"string","format":"objectId","description":"Concrete connection to use in place of the abstract placeholder."}}}}}}}}}}},"prompts":{"type":"array","description":"MCP prompt entries available to the agent. Each item references one MCP connection\nand the prompt names allowed from it. Configured alongside `tools` in the form but\nstored separately; an entry's `allowedPrompts` is what distinguishes a prompt entry\nfrom an MCP tool entry (which carries `allowedTools`).\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","description":"MCP server prompt configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedPrompts":{"type":"array","description":"Prompt names to allow from the MCP server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"}}},"litellm":{"type":"object","description":"LiteLLM proxy configuration. Used when `provider` is \"gemini\" or \"anthropic\".\n\nLiteLLM provides a unified interface to multiple AI providers. Gemini-specific\nsettings are in `_overrides.gemini`; Claude-specific settings are in\n`_overrides.anthropic`.\n\n`model` is required when litellm is the active provider path.\n","properties":{"model":{"type":"string","description":"LiteLLM model identifier. For Gemini, models are stored without the `gemini/`\nprefix; for Anthropic, use the Claude model id (e.g. `claude-sonnet-4-6`)."},"temperature":{"type":"number","minimum":0,"maximum":2,"description":"Sampling temperature"},"maxCompletionTokens":{"type":"number","minimum":100,"maximum":128000,"default":5000,"description":"Maximum number of tokens in the response"},"topP":{"type":"number","minimum":0.1,"maximum":1,"description":"Nucleus sampling parameter"},"seed":{"type":"number","description":"Random seed for reproducible outputs"},"responseFormat":{"type":"object","description":"Output format configuration","properties":{"type":{"type":"string","description":"Output format type.","enum":["text","json_schema","blob"],"default":"text"},"schemaMode":{"type":"string","enum":["manual","json"],"description":"How the structured-output schema was authored in the UI. Editor\nstate only — it does not change how `jsonSchema` is sent to the\nprovider."},"name":{"type":"string","description":"Name for the output format (used with json_schema)."},"strict":{"type":"boolean","description":"When true, enforces strict schema validation on output.","default":false},"jsonSchema":{"type":"object","description":"JSON Schema for structured output. Required when `responseFormat.type` is \"json_schema\".","properties":{"type":{"type":"string","description":"Root JSON Schema type of the structured output; use \"object\" for record-shaped results.","enum":["object","array","string","number","integer","boolean"]},"properties":{"type":"object","additionalProperties":true,"description":"JSON Schema definitions for each field the structured output may contain."},"required":{"type":"array","description":"Property names the model must include in the structured output.","items":{"type":"string"}},"additionalProperties":{"type":"boolean","description":"When true, the structured output may include properties beyond those defined in `properties`."}}}},"if":{"properties":{"type":{"const":"json_schema"}},"required":["type"]},"then":{"required":["name","jsonSchema"]}},"_overrides":{"type":"object","description":"Provider-specific overrides","properties":{"gemini":{"type":"object","description":"Gemini-specific configuration overrides.\n","required":["systemInstruction"],"properties":{"systemInstruction":{"type":"string","maxLength":1000000,"description":"System instruction for Gemini models. Equivalent to OpenAI's `instructions`.\nMaximum 1,000,000 characters.\n"},"tools":{"type":"array","description":"Gemini-specific tools","items":{"type":"object","properties":{"type":{"type":"string","enum":["googleSearch","urlContext","fileSearch","mcp","tool"],"description":"Type of Gemini tool."},"googleSearch":{"type":"object","description":"Google Search configuration (empty object to enable)"},"urlContext":{"type":"object","description":"URL context configuration (empty object to enable)"},"fileSearch":{"type":"object","description":"File search configuration, used when type is \"fileSearch\".","properties":{"fileSearchStoreNames":{"type":"array","description":"Names of the file search stores the model can query.","items":{"type":"string"}}}},"mcp":{"type":"object","description":"MCP server tool configuration, used when type is \"mcp\".","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if\nomitted). Each entry is either a plain tool name\n(legacy form) or an object carrying display metadata\n— same contract as the OpenAI `allowedTools`.","items":{"type":["string","object"],"properties":{"name":{"type":"string","maxLength":256,"description":"Tool name as exposed by the MCP server."},"title":{"type":"string","maxLength":300,"description":"Display title shown for the tool."},"description":{"type":"string","maxLength":1000,"description":"Display description shown for the tool."}},"required":["name"]}},"allowedPrompts":{"type":"array","description":"Specific prompts to allow from the MCP server (used for MCP prompt entries; all if omitted).","items":{"type":"string"}}}},"tool":{"type":"object","description":"Reference to a Celigo Tool resource, used when type is \"tool\".","properties":{"_toolId":{"type":"string","format":"objectId","description":"Reference to the Tool resource."},"overrides":{"type":"object","description":"Per-agent overrides for the tool's internal resources.","properties":{"connections":{"type":"array","description":"Remaps the tool's abstract connections for this agent. Each entry\npairs the tool's abstract connection placeholder (`_abstractId`)\nwith the concrete connection (`_id`) to use for this agent;\nentries without `_id` keep the tool's own default connection.\n","items":{"type":["object","null"],"required":["_abstractId"],"properties":{"_abstractId":{"type":"string","format":"objectId","description":"The tool's abstract connection placeholder being overridden."},"_id":{"type":"string","format":"objectId","description":"Concrete connection to use in place of the abstract placeholder."}}}}}}}}}}},"prompts":{"type":"array","description":"MCP prompt entries available to the Gemini agent. Each item references one\nMCP connection and the prompt names allowed from it. The presence of\n`allowedPrompts` distinguishes a prompt entry from an MCP tool entry.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","description":"MCP server prompt configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedPrompts":{"type":"array","description":"Prompt names to allow from the MCP server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"},"responseModalities":{"type":"array","description":"Response output modalities","items":{"type":"string","enum":["text","image"]},"default":["text"]},"topK":{"type":"number","description":"Top-K sampling parameter for Gemini"},"thinkingConfig":{"type":"object","description":"Controls Gemini's extended thinking capabilities","properties":{"includeThoughts":{"type":"boolean","description":"When true, includes the model's thinking steps in the response."},"thinkingBudget":{"type":"number","minimum":100,"maximum":4000,"description":"Maximum tokens allocated for thinking"},"thinkingLevel":{"type":"string","description":"Controls how much thinking effort the model applies; use higher levels for complex, multi-step tasks at the cost of latency and tokens.","enum":["minimal","low","medium","high"]}}},"imageConfig":{"type":"object","description":"Gemini image generation configuration","properties":{"aspectRatio":{"type":"string","description":"Aspect ratio of generated images; choose a ratio matching the intended display format.","enum":["1:1","2:3","3:2","3:4","4:3","4:5","5:4","9:16","16:9","21:9"]},"imageSize":{"type":"string","description":"Output resolution of generated images; higher resolutions increase detail and file size.","enum":["1K","2K","4K"]}}},"mediaResolution":{"type":"string","enum":["low","medium","high"],"description":"Resolution for media inputs (images, video)"}}},"anthropic":{"type":"object","description":"Claude-specific configuration overrides. Used when `provider` is \"anthropic\".\n","required":["systemInstruction"],"properties":{"systemInstruction":{"type":"string","maxLength":1000000,"description":"System instruction for Claude models. Equivalent to OpenAI's `instructions`.\nMaximum 1,000,000 characters.\n"},"topK":{"type":"number","minimum":0,"description":"Top-K sampling parameter. Deprecated on Claude models released after Claude Opus\n4.6, which reject any value at runtime; set it only on older models."},"thinkingConfig":{"type":"object","description":"Controls Claude's extended thinking capabilities.","properties":{"type":{"type":"string","enum":["enabled","disabled","adaptive"],"default":"disabled","description":"Extended thinking mode."},"budgetTokens":{"type":"number","minimum":1024,"description":"Maximum tokens allocated for thinking. Required when `type` is \"enabled\" —\nomitting it fails the save with 422 `invalid_thinking_config`."},"display":{"type":"string","enum":["summarized","omitted"],"default":"summarized","description":"How thinking output is surfaced in the response."},"effort":{"type":"string","enum":["low","medium","high","xhigh","max"],"description":"How much thinking effort the model applies; use with `type` \"adaptive\". Higher\nvalues (`xhigh`, `max`) may be gated to specific Claude models by the provider."}},"if":{"properties":{"type":{"const":"enabled"}},"required":["type"]},"then":{"required":["budgetTokens"]}},"serviceTier":{"type":"string","enum":["auto","standard_only"],"default":"auto","description":"Anthropic service tier for the request."},"tools":{"type":"array","description":"Claude-specific tools.","items":{"type":"object","properties":{"type":{"type":"string","enum":["tool","mcp","webSearch"],"description":"Type of Claude tool."},"tool":{"type":"object","description":"Reference to a Celigo Tool resource, used when type is \"tool\".","properties":{"_toolId":{"type":"string","format":"objectId","description":"Reference to the Tool resource."},"overrides":{"type":"object","description":"Per-agent overrides for the tool's internal resources.","properties":{"connections":{"type":"array","description":"Remaps the tool's abstract connections for this agent. Each entry\npairs the tool's abstract connection placeholder (`_abstractId`)\nwith the concrete connection (`_id`) to use for this agent;\nentries without `_id` keep the tool's own default connection.\n","items":{"type":["object","null"],"required":["_abstractId"],"properties":{"_abstractId":{"type":"string","format":"objectId","description":"The tool's abstract connection placeholder being overridden."},"_id":{"type":"string","format":"objectId","description":"Concrete connection to use in place of the abstract placeholder."}}}}}}}},"mcp":{"type":"object","description":"MCP server tool configuration, used when type is \"mcp\".","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedTools":{"type":"array","description":"Specific tools to allow from the MCP server (all if omitted).","items":{"type":"string"}}}},"webSearch":{"type":"object","description":"Web search configuration, used when type is \"webSearch\".","properties":{"version":{"type":"string","pattern":"^\\d{8}$","description":"Anthropic web search tool version (YYYYMMDD). Selects the tool version sent\non the wire; unsupported versions surface as an Anthropic 400."},"allowedDomains":{"type":"array","description":"Domains the search may return results from. Mutually exclusive with `blockedDomains`.","items":{"type":"string"}},"blockedDomains":{"type":"array","description":"Domains to exclude from search results. Mutually exclusive with `allowedDomains`.","items":{"type":"string"}},"userLocation":{"type":"object","description":"Approximate user location used to localize search results. When present, at\nleast one of `city`, `country`, `region`, or `timezone` must be set.","properties":{"type":{"type":"string","enum":["approximate"],"description":"Location type. Always \"approximate\"."},"city":{"type":"string","maxLength":256,"description":"City name for localizing search results."},"country":{"type":"string","maxLength":8,"description":"ISO 3166-1 alpha-2 country code for localizing search results."},"region":{"type":"string","maxLength":256,"description":"Region or state for localizing search results."},"timezone":{"type":"string","maxLength":64,"description":"IANA timezone for localizing search results."}}}}}}}},"prompts":{"type":"array","description":"MCP prompt entries available to the Claude agent. Each item references one MCP\nconnection and the prompt names allowed from it. The presence of `allowedPrompts`\ndistinguishes a prompt entry from an MCP tool entry.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of prompt entry. Always \"mcp\"."},"mcp":{"type":"object","description":"MCP server prompt configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server."},"allowedPrompts":{"type":"array","description":"Prompt names to allow from the MCP server.","items":{"type":"string"}}}}}}},"resources":{"$ref":"#/components/schemas/McpResources"}}}}}}}},"if":{"properties":{"provider":{"const":"openai"}},"required":["provider"]},"then":{"required":["openai"],"properties":{"openai":{"required":["model","instructions"]}}},"else":{"if":{"properties":{"provider":{"enum":["gemini","anthropic"]}},"required":["provider"]},"then":{"required":["litellm"],"properties":{"litellm":{"required":["model"]}}},"else":{"if":{"properties":{"provider":{"enum":["mistral","xai","huggingface","deepseek","cohere","groq"]}},"required":["provider"]},"then":{"required":["model"]}}}},"McpResources":{"type":"array","description":"Governed MCP resources — read-only reference content (policies, schemas, documentation)\npulled from connected MCP servers and made available to the agent as a consistent source\nof truth. Each entry references one MCP connection and the specific resources allowed from it.","items":{"type":"object","required":["type","mcp"],"properties":{"type":{"type":"string","enum":["mcp"],"description":"Type of resource entry. Always \"mcp\"."},"mcp":{"type":"object","required":["_mcpConnectionId","allowedResources"],"description":"MCP server resource configuration.","properties":{"_mcpConnectionId":{"type":"string","format":"objectId","description":"Connection to the MCP server that exposes the resources."},"allowedResources":{"type":"array","minItems":1,"description":"Resources to expose to the agent from the MCP server. Must contain at least one\nentry; each entry identifies one resource by name and URI.","items":{"type":"object","required":["name","uri"],"properties":{"name":{"type":"string","description":"Display name of the MCP resource."},"uri":{"type":"string","description":"URI that identifies the resource on the MCP server."}}}}}}}}},"GuardrailConfig":{"type":"object","description":"Configuration for GuardrailImport adaptor type.\n\nGuardrails evaluate data flowing through integrations for safety and\ncompliance. The `type` field selects which check to apply, and the\ncorresponding sub-object (`aiAgent`, `pii`, or `moderation`) provides\nthe configuration.\n\nA `_connectionId` on the parent import is only needed for BYOK\n`ai_agent` guardrails. In responses the server echoes the active type's\nsub-object and applies the `confidenceThreshold` default; it also\nreturns inactive sibling sub-objects (e.g. `moderation: {categories: []}`\non a `pii` guardrail, or a populated `pii` left over from a type switch),\nbut only the active type's sub-object is meaningful. Legacy documents\nmay carry a server-written default `aiAgent` stub on `pii`/`moderation`\nguardrails; current servers strip the inactive `aiAgent` on write.","properties":{"type":{"type":"string","enum":["ai_agent","pii","moderation"],"description":"The type of guardrail to apply. Each type requires its corresponding\nsub-configuration object (`aiAgent`, `pii`, or `moderation`)."},"confidenceThreshold":{"type":"number","minimum":0,"maximum":1,"default":0.7,"description":"Confidence threshold (0 to 1). Detections below this threshold are\nignored. Lower values catch more issues but increase false positives."},"aiAgent":{"type":"object","description":"AI agent check configuration; set when `type` is `ai_agent`. On\n`pii`/`moderation` guardrails a legacy server-written stub may\nappear here — it is inert, and current servers strip it on write."},"pii":{"type":"object","required":["entities"],"description":"PII detection configuration. Required when `type` is `pii`.","properties":{"entities":{"type":"array","description":"PII entity types to detect. When `type` is `pii`, at least one\nentry is required; the inactive sibling on other guardrail types\nmay be served with an empty list.","items":{"type":"string","enum":["credit_card_number","card_security_code_cvv_cvc","cryptocurrency_wallet_address","date_and_time","email_address","iban_code","bic_swift_bank_identifier_code","ip_address","location","medical_license_number","national_registration_number","persons_name","phone_number","url","us_bank_account_number","us_drivers_license","us_itin","us_passport_number","us_social_security_number","uk_nhs_number","uk_national_insurance_number","spanish_nif","spanish_nie","italian_fiscal_code","italian_drivers_license","italian_vat_code","italian_passport","italian_identity_card","polish_pesel","finnish_personal_identity_code","singapore_nric_fin","singapore_uen","australian_abn","australian_acn","australian_tfn","australian_medicare","indian_pan","indian_aadhaar","indian_vehicle_registration","indian_voter_id","indian_passport","korean_resident_registration_number"]}},"mask":{"type":"boolean","default":false,"description":"When true, detected PII is replaced with masked values.\nWhen false, PII is flagged without modification."}}},"moderation":{"type":"object","required":["categories"],"description":"Content moderation configuration. Required when `type` is `moderation`.","properties":{"categories":{"type":"array","description":"Content moderation categories to check. When `type` is\n`moderation`, at least one entry is required; the inactive\nsibling on other guardrail types may be served with an empty\nlist.","items":{"type":"string","enum":["sexual","sexual_minors","hate","hate_threatening","harassment","harassment_threatening","self_harm","self_harm_intent","self_harm_instructions","violence","violence_graphic","illicit","illicit_violent"]}}}}},"required":["type","confidenceThreshold"],"if":{"required":["type"],"properties":{"type":{"const":"pii"}}},"then":{"required":["pii"],"properties":{"pii":{"required":["entities"],"properties":{"entities":{"minItems":1}}}}},"else":{"if":{"required":["type"],"properties":{"type":{"const":"moderation"}}},"then":{"required":["moderation"],"properties":{"moderation":{"required":["categories"],"properties":{"categories":{"minItems":1}}}}},"else":{"required":["aiAgent"],"properties":{"aiAgent":{"$ref":"#/components/schemas/AiAgentConfig"}}}}},"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"},"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\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**: Using wildcards like `*` and `?`\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- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"isempty\"`: Field is empty or null (is empty)\n- `\"isnotempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches specified pattern (matches)\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- Returns the raw field value which may need type conversion\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  - `export.settingName`: Access export-level settings\n  - `import.settingName`: Access import-level settings\n  - `integration.settingName`: Access integration-level settings\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\nType conversion operators are often necessary when comparing extracted field values against literals or when the field type doesn't match the comparison operator's expected type. 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    \"extract\",\n    \"status\"\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"extract\",\n    \"filename\"\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\n```json\n[\n  \"greaterthan\",\n  [\n    \"extract\",\n    \"orderDate\"\n  ],\n  \"2023-01-01T00:00:00Z\"\n]\n```\n\nExample 5: Category contains any of [\"Urgent\", \"High Priority\"]\n```json\n[\n  \"anyof\",\n  [\n    \"extract\",\n    \"category\"\n  ],\n  [\"Urgent\", \"High Priority\"]\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 that exists in the system.\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"}}}}},"Hook":{"type":"object","description":"A single lifecycle hook — a JavaScript function invoked from a script (or hosted on a\nstack) at a fixed point in an import or AI-agent run. Shared by every hook slot; the\nslot's own description says when in the lifecycle it fires.","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","null"],"description":"Static parameters passed to the hook function at runtime, letting one script be\nreused with different settings. Null when the hook carries no static parameters."}}},"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.\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"}}}}},"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, arrays of objects, arrays of arrays of objects) are ALL\n   handled through this recursive pattern\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","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- For arrays within arrays: Leave EMPTY for the inner array mappings, as they don't have field names\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, empty 'generate' fields almost always indicate inner array structures\nwithin a parent array.\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, attempts conversion from strings\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. REQUIRES child mappings in the 'mappings' array\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- `arrayarray`: Array of arrays (for matrix/table structures)\n\nArray dataTypes can be populated two ways: pass a source array through\nunchanged via `extract` alone (when the source is already an array of\nthe right shape), or construct/iterate via `buildArrayHelper`.\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` is one option (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","arrayarray"]},"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\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 `object`, `objectarray`, and primitive-array dataTypes as\n  well as scalars — the rendered template output must parse into the\n  declared shape\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- When both extract and mappings are defined for objects, extract is applied first\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 empty (the UI's \"Do nothing\" action).\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.\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"},"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, date)\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 REQUIRED mechanism for ALL array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.)\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**For Arrays of Arrays (arrayarray)**\n- Similar to objectarray, but inner arrays have empty 'generate' fields\n- Used for matrix/table structures\n```json\n{\n  \"generate\": \"matrix\",\n  \"dataType\": \"arrayarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.rows[*]\",  // For each row in the rows array\n      \"mappings\": [\n        {\n          \"dataType\": \"numberarray\",  // Note: No generate field for inner arrays\n          \"buildArrayHelper\": [\n            {\"extract\": \"$.rows.columns[*]\"}  // Notice: \"rows\" is singular in the composite object\n          ]\n        }\n      ]\n    }\n  ]\n}\n```\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"},"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":"Specifies the condition that must be met for an array element to be included.\n\n'extract_not_empty' only includes elements where the extract field returns a value.\n","enum":["extract_not_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"}}}}},"status":{"type":"string","description":"**Purpose**\nRequired on every mapping entry. Controls whether the mapping is applied.\n\n**Guidance**\nEmit `\"Active\"` for mappings that should run; `\"Draft\"` saves an\nin-progress mapping without the Active-only field validations. The\nAPI rejects a mapping missing this field\n(validation error: \"Mapping object must have status field present.\").\n","enum":["Active","Draft"]},"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**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":"Specifies the condition that triggers application of this mapping:\n- record_created: Apply only when creating a new record\n- record_updated: Apply only when updating an existing record\n- extract_not_empty: Apply only when the extract field returns a value\n","enum":["record_created","record_updated","extract_not_empty"]}}}},"required":["dataType"]}},"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- For arrays within arrays: Leave EMPTY for the inner array mappings, as they don't have field names\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, empty 'generate' fields almost always indicate inner array structures\nwithin a parent array.\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, attempts conversion from strings\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. REQUIRES child mappings in the 'mappings' array\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- `arrayarray`: Array of arrays (for matrix/table structures)\n\nArray dataTypes can be populated two ways: pass a source array through\nunchanged via `extract` alone (when the source is already an array of\nthe right shape), or construct/iterate via `buildArrayHelper`.\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` is one option (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","arrayarray"]},"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\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 `object`, `objectarray`, and primitive-array dataTypes as\n  well as scalars — the rendered template output must parse into the\n  declared shape\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- When both extract and mappings are defined for objects, extract is applied first\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 empty (the UI's \"Do nothing\" action).\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.\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"},"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, date)\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 REQUIRED mechanism for ALL array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.)\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**For Arrays of Arrays (arrayarray)**\n- Similar to objectarray, but inner arrays have empty 'generate' fields\n- Used for matrix/table structures\n```json\n{\n  \"generate\": \"matrix\",\n  \"dataType\": \"arrayarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.rows[*]\",  // For each row in the rows array\n      \"mappings\": [\n        {\n          \"dataType\": \"numberarray\",  // Note: No generate field for inner arrays\n          \"buildArrayHelper\": [\n            {\"extract\": \"$.rows.columns[*]\"}  // Notice: \"rows\" is singular in the composite object\n          ]\n        }\n      ]\n    }\n  ]\n}\n```\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"},"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":"Specifies the condition that must be met for an array element to be included.\n\n'extract_not_empty' only includes elements where the extract field returns a value.\n","enum":["extract_not_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"}}}}},"status":{"type":"string","description":"**Purpose**\nRequired on every mapping entry. Controls whether the mapping is applied.\n\n**Guidance**\nEmit `\"Active\"` for mappings that should run; `\"Draft\"` saves an\nin-progress mapping without the Active-only field validations. The\nAPI rejects a mapping missing this field\n(validation error: \"Mapping object must have status field present.\").\n","enum":["Active","Draft"]},"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**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":"Specifies the condition that triggers application of this mapping:\n- record_created: Apply only when creating a new record\n- record_updated: Apply only when updating an existing record\n- extract_not_empty: Apply only when the extract field returns a value\n","enum":["record_created","record_updated","extract_not_empty"]}}}},"required":["dataType"]},"MappingField":{"type":"object","description":"One Mapper 1.0 field-mapping entry.","properties":{"generate":{"type":"string","description":"Target field path to write on the destination record."},"extract":{"type":"string","description":"Source expression. Accepts:\n- JSONPath starting with `$.` (e.g. `$.customer.firstName`).\n- Handlebars template (contains `{{`) for concatenation /\n  formatting / conditional logic (e.g.\n  `{{record.firstName}} {{record.lastName}}`).\n- A literal string (any value not starting with `$.` and not\n  containing `{{`) — treated as a hardcoded value.\n\nOmit when using `hardCodedValue`.\n"},"hardCodedValue":{"type":["string","null"],"description":"Static value written to `generate` instead of extracting from the source record."},"dataType":{"type":"string","enum":["string","number","boolean","numberarray","stringarray","json"],"description":"Data type coercion applied to the mapped value."},"discardIfEmpty":{"type":"boolean","description":"When true, the field is omitted from the output when the extracted value is empty."},"immutable":{"type":"boolean","description":"When true, the mapped value cannot be overwritten by later mapping steps."},"lookupName":{"type":"string","description":"Name of an entry in the import's `lookups` array used to translate the value."},"default":{"type":["string","null"],"description":"Fallback value used when the extract yields no value."},"extractDateFormat":{"type":"string","description":"Date format of the source value, used to parse it before conversion."},"extractDateTimezone":{"type":"string","description":"Timezone applied when parsing the source date value."},"generateDateFormat":{"type":"string","description":"Date format applied to the value written to the destination."},"generateDateTimezone":{"type":"string","description":"Timezone applied when formatting the destination date value."},"conditional":{"type":"object","description":"Only apply this mapping entry when the given condition is satisfied.\n","properties":{"when":{"type":"string","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","expression"],"description":"Condition that gates whether this mapping entry is applied.\n`lookup_not_empty` / `lookup_empty` evaluate the lookup named by the\nsibling `lookupName`; `expression` evaluates the sibling `expression`.\n"},"lookupName":{"type":"string","description":"Lookup to evaluate for the `lookup_not_empty` / `lookup_empty`\nconditions.\n"},"expression":{"type":"string","description":"Expression evaluated when `when` is `expression`.\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.\nThis value typically matches the key in the fieldMap object.\n"},"name":{"type":"string","description":"Name of the field, used as the property name when generating the settings object\nfrom the submitted form data.\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.\n","default":false},"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 fields, specifies the placeholder and field name for the key input.\n"},"valueName":{"type":"string","description":"For keyvalue fields, specifies the placeholder and field name for the value input.\n"},"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.\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"]},"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.\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","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. This script must be accessible within the user's account\nand have appropriate permissions.\n"}}}}},"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"}}},"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},"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":"Reference to the integration app that defines this resource.\n\nThis field is only populated for resources that are part of an integration app.\nIt contains the unique identifier (_id) of the integration app (connector)\nthat defines the structure, behavior, and templates for this resource.\n\nThe integration app is the published template that can be installed\nmultiple times across different accounts, with each installation creating\na separate integration instance (referenced by _integrationId).\n\nThis reference enables:\n- Identifying the source integration app for this resource\n- Determining which template version is being used\n- Linking to documentation, support, and marketplace information\n"}}},"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/imports/{_id}/clone":{"post":{"summary":"Clone an import","description":"Creates a copy of an existing import.\nSupports optionally remapping referenced connections (via connectionMap).\n","operationId":"cloneImport","tags":["Imports"],"parameters":[{"name":"_id","in":"path","description":"The unique identifier of the import to clone","required":true,"schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloneRequest"}}}},"responses":{"201":{"description":"Import 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"}}}}}}
````

## Preview cloning an import

> Returns a preview of the resources that would be created by cloning the specified import.\
> The response includes the target import and any transitive dependencies (e.g. connections, scripts).\
> No resources are created by this endpoint.<br>

```json
{"openapi":"3.2.0","info":{"title":"Imports","version":"1.0.0"},"tags":[{"name":"Imports","description":"Imports deliver data to destination systems. An import receives pages of records from\nupstream flow steps, applies field mappings and transformations, and writes the results\nto the target application, database, or file destination — handling lookups, duplicate\navoidance, and per-record error reporting along the way. Each import uses one\n`adaptorType` that determines its connection compatibility and configuration object.\n\n## Import schema\n\n{% openapi-schemas spec=\"import\" schemas=\"Import\" 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":{"ClonePreviewResponse":{"type":"object","description":"Preview of the resources that would be created by a clone operation.\nEach object in the `objects` array represents a resource that will be\ncloned, including the target resource and all transitive dependencies\n(connections, scripts, exports, imports, etc.).\n","properties":{"objects":{"type":"array","description":"List of resources that would be created by the clone. Always includes\nthe target resource and may include transitive dependencies such as\nconnections, scripts, exports, imports, async helpers, and lookup caches.\n","items":{"type":"object","properties":{"model":{"type":"string","description":"The model type of the resource. Observed values include\nAsyncHelper, Connection, Export, Flow, Import, Integration,\nLookupCache, Script, and Tool.\n"},"doc":{"type":"object","description":"The full resource document that would be created by the clone.","additionalProperties":true}}}},"stackRequired":{"type":"boolean","description":"Whether the clone requires a stack (connector-level) environment to proceed."},"_stackId":{"type":["string","null"],"description":"The stack id associated with the resource, or null if no stack is involved."}}},"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/imports/{_id}/clone/preview":{"get":{"summary":"Preview cloning an import","description":"Returns a preview of the resources that would be created by cloning the specified import.\nThe response includes the target import and any transitive dependencies (e.g. connections, scripts).\nNo resources are created by this endpoint.\n","operationId":"previewCloneImport","tags":["Imports"],"parameters":[{"name":"_id","in":"path","description":"The unique identifier of the import to preview cloning","required":true,"schema":{"type":"string","format":"objectId"}}],"responses":{"200":{"description":"Clone preview retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClonePreviewResponse"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
```

## Replace connection on import for a branched flow

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

```json
{"openapi":"3.2.0","info":{"title":"Imports","version":"1.0.0"},"tags":[{"name":"Imports","description":"Imports deliver data to destination systems. An import receives pages of records from\nupstream flow steps, applies field mappings and transformations, and writes the results\nto the target application, database, or file destination — handling lookups, duplicate\navoidance, and per-record error reporting along the way. Each import uses one\n`adaptorType` that determines its connection compatibility and configuration object.\n\n## Import schema\n\n{% openapi-schemas spec=\"import\" schemas=\"Import\" 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/imports/{_id}/replaceConnection":{"put":{"summary":"Replace connection on import for a branched flow","description":"Replaces the connection used by an import in a flow and cancels any related running jobs.\nThis is useful when migrating flows between environments or updating to newer connection versions.\n","operationId":"replaceConnectionOnImport","tags":["Imports"],"parameters":[{"name":"_id","in":"path","description":"The unique identifier of the import","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 import"},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
```

## Invoke an import with data and return per-record results

> Runs an existing import against the destination system with the supplied\
> data records and returns per-record results synchronously.\
> \
> The request body should contain a \`data\` array of records to import. Each\
> record is processed through the import's mappings, transformations, and\
> hooks before being sent to the destination.\
> \
> The response is an \*\*array\*\* of per-record result objects, each containing\
> a \`statusCode\`, the transformed \`\_json\` payload, and any \`errors\`\
> encountered during processing. This endpoint writes to the destination\
> system — and so does \`POST /v1/imports/preview\` (verified to execute the\
> composed request), so neither is a dry run against a production\
> destination. A 200 response may still contain per-record failures in\
> each element's \`errors\` array.

```json
{"openapi":"3.2.0","info":{"title":"Imports","version":"1.0.0"},"tags":[{"name":"Imports","description":"Imports deliver data to destination systems. An import receives pages of records from\nupstream flow steps, applies field mappings and transformations, and writes the results\nto the target application, database, or file destination — handling lookups, duplicate\navoidance, and per-record error reporting along the way. Each import uses one\n`adaptorType` that determines its connection compatibility and configuration object.\n\n## Import schema\n\n{% openapi-schemas spec=\"import\" schemas=\"Import\" 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"]}}}},"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"}}}}},"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/imports/{_id}/invoke":{"post":{"operationId":"invokeImport","tags":["Imports"],"summary":"Invoke an import with data and return per-record results","description":"Runs an existing import against the destination system with the supplied\ndata records and returns per-record results synchronously.\n\nThe request body should contain a `data` array of records to import. Each\nrecord is processed through the import's mappings, transformations, and\nhooks before being sent to the destination.\n\nThe response is an **array** of per-record result objects, each containing\na `statusCode`, the transformed `_json` payload, and any `errors`\nencountered during processing. This endpoint writes to the destination\nsystem — and so does `POST /v1/imports/preview` (verified to execute the\ncomposed request), so neither is a dry run against a production\ndestination. A 200 response may still contain per-record failures in\neach element's `errors` array.","parameters":[{"in":"path","name":"_id","required":true,"schema":{"type":"string","format":"objectId"},"description":"Import ID"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","description":"Records to import","properties":{"data":{"type":"array","description":"Array of records to send to the destination system","items":{"type":"object","additionalProperties":true}}},"additionalProperties":true}}}},"responses":{"200":{"description":"Import completed. Returns a per-record result array. Each element\ncontains the `statusCode` from the destination, the transformed\n`_json` payload, and any `errors` encountered.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"statusCode":{"type":"integer","description":"HTTP status code from the destination system"},"_json":{"type":"object","description":"The transformed record as sent to the destination","additionalProperties":true},"errors":{"type":"array","description":"Errors encountered processing this record","items":{"type":"object","properties":{"source":{"type":"string"},"code":{"type":"string"},"message":{"type":"string"},"resolved":{"type":"boolean"},"occurredAt":{"type":"integer","format":"int64"},"stage":{"type":"string"},"classification":{"type":"string"}}}}}}}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"description":"Import not found","content":{"application/json":{"schema":{"type":"object","properties":{"errors":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
```

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

> Runs an import doc through the flow engine's preview pipeline against\
> supplied sample data and returns the per-stage output. \*\*The composed\
> request IS executed against the live destination\*\* — previewing a\
> create/update import against a production system writes real records\
> (a saved \`mockResponse\` does NOT protect this surface; it substitutes\
> only in flow test runs and flow-builder previews), so point previews\
> at sandbox destinations when a live write is not acceptable. There is\
> no request option that disables the send: flags such as \`preview\`,\
> \`sendAndPreview\`, or \`send\` — top level, inside an \`options\` object,\
> or on the import doc — are silently ignored and the request executes\
> regardless. For a compose-only preview of the destination request,\
> use \`POST /v1/pageProcessors/preview\` with \`{preview: true}\` on the\
> target entry (requires a flow context). No Job record is created and\
> no flow-level state is updated.\
> \
> The integrator.io UI never calls this path — the import editor's\
> "Preview" and "Send" buttons both go through\
> \`POST /v1/pageProcessors/preview\`. This endpoint is the API-only,\
> unscoped counterpart to\
> \`POST /v1/integrations/{\_integrationId}/flows/{\_flowId}/imports/preview\`\
> — prefer this variant when previewing a standalone import that is not\
> yet associated with a flow.

```json
{"openapi":"3.2.0","info":{"title":"Imports","version":"1.0.0"},"tags":[{"name":"Imports","description":"Imports deliver data to destination systems. An import receives pages of records from\nupstream flow steps, applies field mappings and transformations, and writes the results\nto the target application, database, or file destination — handling lookups, duplicate\navoidance, and per-record error reporting along the way. Each import uses one\n`adaptorType` that determines its connection compatibility and configuration object.\n\n## Import schema\n\n{% openapi-schemas spec=\"import\" schemas=\"Import\" 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":{"ImportPreviewResponse":{"type":"object","description":"Envelope returned by `POST /v1/imports/preview` (and the scoped\n`/v1/integrations/{_integrationId}/flows/{_flowId}/imports/preview`\nvariant). Carries per-stage diagnostics alongside the sampled records\nproduced by running the supplied source data through the import's\nmapping/transform/target pipeline. The target stage executes the\ncomposed destination request — see the operation descriptions for the\nwrite-safety warning.","properties":{"data":{"type":"array","description":"Final sampled records emitted by the preview — the output of the last\npipeline stage. Mirrors `stages[-1].data` and is exposed here as a\nconvenience for callers that only want the end result.","items":{}},"stages":{"type":"array","description":"Ordered list of pipeline stages the preview traversed. **Absent** when\nthe request body's `data[]` was empty (minimal response is\n`{data:[null]}` with no `stages`). Stage names are adaptor-dependent:\n- **AI Agent imports** produce `request` → `raw` → `parse` (the\n  request sent to the model, the raw model response, and the parsed\n  output).\n- **HTTP imports** typically produce `request` → `response` → hook\n  stages such as `postResponseHook`.\n- **Transform-only stages** (e.g. Mapper 2.0) appear between the\n  source-shaping stages and the final `request` stage.\n\nEach stage carries its output `data[]` and any `errors[]`/`null`\nraised at that stage. Stage-level errors do NOT fail the overall\ncall — they surface here rather than failing the overall request.","items":{"type":"object","description":"One pipeline stage's diagnostic and output envelope.","properties":{"name":{"type":"string","description":"Stage identifier (e.g. `request`, `raw`, `parse`, `postResponseHook`)."},"data":{"description":"Stage output. Shape varies by stage and adaptor — `request`\nstages typically carry the prepared request payload, `raw`\nstages carry the unparsed vendor response, `parse` stages\ncarry the structured output.","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 non-null\n`stages[].errors[]` entries. Often omitted entirely when the preview\nran clean; callers should treat `undefined` and `[]` as equivalent.","items":{"type":"object","additionalProperties":true}}}},"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/imports/preview":{"post":{"operationId":"previewImport","tags":["Imports"],"summary":"Preview the output of an import doc (no job created)","description":"Runs an import doc through the flow engine's preview pipeline against\nsupplied sample data and returns the per-stage output. **The composed\nrequest IS executed against the live destination** — previewing a\ncreate/update import against a production system writes real records\n(a saved `mockResponse` does NOT protect this surface; it substitutes\nonly in flow test runs and flow-builder previews), so point previews\nat sandbox destinations when a live write is not acceptable. There is\nno request option that disables the send: flags such as `preview`,\n`sendAndPreview`, or `send` — top level, inside an `options` object,\nor on the import doc — are silently ignored and the request executes\nregardless. For a compose-only preview of the destination request,\nuse `POST /v1/pageProcessors/preview` with `{preview: true}` on the\ntarget entry (requires a flow context). No Job record is created and\nno flow-level state is updated.\n\nThe integrator.io UI never calls this path — the import editor's\n\"Preview\" and \"Send\" buttons both go through\n`POST /v1/pageProcessors/preview`. This endpoint is the API-only,\nunscoped counterpart to\n`POST /v1/integrations/{_integrationId}/flows/{_flowId}/imports/preview`\n— prefer this variant when previewing a standalone import that is not\nyet associated with a flow.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Envelope containing the import document to preview plus the\nsample records to feed it.","properties":{"import":{"type":"object","description":"Full import document (mirror of `POST /v1/imports` body\nshape), optionally carrying an `_id` when previewing an\nalready-saved import. All adaptor-specific fields\n(`aiAgent`, `http`, `rdbms`, ...) apply exactly as they\nwould on a live import.","additionalProperties":true},"data":{"type":"array","description":"Sample source records to run through the import pipeline.\nPass `[{}]` for adaptors that don't require an input record\n(e.g. AI Agent imports whose prompt generates the payload).\nAn **empty array** (`[]`) is accepted but produces a minimal\n`{data:[null]}` response with no `stages`. **Omitting `data`\nentirely returns HTTP 204** — the engine short-circuits when\nit has no records to trace.","items":{"type":"object","additionalProperties":true}}},"required":["import"]}}}},"responses":{"200":{"description":"Preview executed. Stage-level errors in the import config surface in\n`stages[].errors` and the top-level `errors[]`; inspect those before\ntrusting `data[]`. When `data:[]` was supplied, the response is a\nminimal `{data:[null]}` with no `stages` block.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportPreviewResponse"}}}},"204":{"description":"No content — returned when the request body omits the `data` key\nentirely (the engine has nothing to trace). Supply `data:[{}]` or\n`data:[...]` to get a populated preview envelope."},"401":{"$ref":"#/components/responses/401-unauthorized"},"422":{"description":"Request body is malformed — most commonly the top-level `import`\nobject is missing — or the adaptor type is unsupported: RDBMS-family\nimports are rejected with `invalid_adaptor` (\"Import preview is not\nsupported for adaptor type: RDBMSImport\"); database imports have no\nrequest-preview surface anywhere (the SQL editor's Handlebars\nevaluation is their only preview). Structural validation returns the\nstandard errors envelope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Preview NetSuite import field mappings against sample data

> Resolves a NetSuite import's Mapper 1.0 field mappings against supplied\
> sample records and returns the mapped NetSuite record — the body fields\
> (\`nlobjFieldIds\`) and sublists (\`nlobjSublistIds\`) that \*would\* be sent —\
> without writing anything to NetSuite. Handlebars expressions in the mapping\
> are evaluated, so this is the way to confirm how a mapping resolves before\
> running it live.\
> \
> This is a stateless dry-run of the mapping engine: no Job is created and no\
> NetSuite record is touched. It is the NetSuite-mapping counterpart to\
> \`POST /v1/imports/preview\`, which previews the full import pipeline; use this\
> operation when you specifically want the resolved NetSuite field values.

```json
{"openapi":"3.2.0","info":{"title":"Imports","version":"1.0.0"},"tags":[{"name":"Imports","description":"Imports deliver data to destination systems. An import receives pages of records from\nupstream flow steps, applies field mappings and transformations, and writes the results\nto the target application, database, or file destination — handling lookups, duplicate\navoidance, and per-record error reporting along the way. Each import uses one\n`adaptorType` that determines its connection compatibility and configuration object.\n\n## Import schema\n\n{% openapi-schemas spec=\"import\" schemas=\"Import\" 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":{"MappingPreviewRequest":{"type":"object","description":"Envelope for previewing how a NetSuite import's Mapper 1.0 field mappings\nresolve against sample source records. Carries the sample data plus the\nNetSuite Distributed (`netsuite_da`) import configuration whose `mapping`\nis applied.","required":["data","importConfig"],"properties":{"data":{"type":"array","description":"Sample source records to run the mappings against. Each record is the\ninput the mapper extracts from (the shape an upstream export would emit).\nHandlebars expressions in `importConfig.mapping` are evaluated per record.","items":{"type":"object","additionalProperties":true}},"importConfig":{"description":"NetSuite Distributed import configuration to preview — the same object\nshape as an import's `netsuite_da`, plus the Mapper 1.0 `mapping` and\n`lookups` that drive the field resolution.","allOf":[{"$ref":"#/components/schemas/NetsuiteDistributed"},{"type":"object","properties":{"mapping":{"type":"object","description":"Mapper 1.0 mapping configuration applied to each record. `fields`\nmaps individual fields; `lists` maps repeating sublists.","properties":{"fields":{"type":"array","description":"Field-level mapping entries applied to each record.","items":{"$ref":"#/components/schemas/MappingField"}},"lists":{"type":"array","description":"List-level mappings, each generating a NetSuite sublist on the target record.","items":{"type":"object","properties":{"generate":{"type":"string","description":"Target sublist to generate on the NetSuite record."},"fields":{"type":"array","items":{"$ref":"#/components/schemas/MappingField"}}}}}}},"lookups":{"type":"array","description":"Lookup definitions referenced by `mapping.fields[].lookupName` to\ntranslate source values into NetSuite values during the preview.","items":{"type":"object","additionalProperties":true}}}}]},"celigo_resource":{"type":"string","const":"previewImportMappingFields","description":"Resource discriminator the mapping engine uses to route the request.\nAlways `previewImportMappingFields` for this operation."}}},"NetsuiteDistributed":{"type":"object","description":"Configuration for NetSuite Distributed (SuiteApp 2.0) import operations — the primary sub-schema for the NetSuiteDistributedImport adaptorType. The API field name is \"netsuite_da\".\noperation and recordType are required. The server may attach empty bookkeeping stubs of\nthis object (empty lookups/mapping, or missingOrCorruptedDAConfig) to imports of other\nadaptor types; stubs omit these fields. Set internalIdLookup so existing records\ncan be found when operation is update, addupdate, or delete.","properties":{"operation":{"type":"string","enum":["add","update","addupdate","attach","detach","delete"],"description":"Operation to perform on the target NetSuite record, as a plain string. Required.\nFor `update`, `addupdate`, and `delete`, also set internalIdLookup so existing records can be found."},"recordType":{"type":"string","description":"NetSuite record type to import into (e.g. \"customer\", \"salesorder\", \"customrecord_myrecord\" for custom record types). Required.\nMust match a valid NetSuite record type identifier."},"recordIdentifier":{"type":"string","description":"Custom record identifier, used to identify the specific record type when\nimporting into custom record types.\n"},"restletVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"Version of the NetSuite RESTlet to use, as a plain string.\nDefaults to \"suiteapp2.0\" when useSS2Restlets is true, \"suitebundle\" otherwise; rarely needs to be set explicitly for modern integrations.\nThe version is fixed when the step is created — the Advanced selector is disabled on existing steps, and migrating an existing step to a different version means recreating or cloning it."},"useSS2Restlets":{"type":"boolean","description":"When true, uses SuiteScript 2.0 RESTlets and restletVersion defaults to \"suiteapp2.0\".\nDefaults to true for modern integrations.\nSet at step creation together with restletVersion — the same creation-time-only constraint applies (see restletVersion)."},"missingOrCorruptedDAConfig":{"type":"boolean","description":"When true, the Distributed Adaptor configuration is missing or corrupted.\nSet by the system — do not set manually."},"batchSize":{"type":"number","description":"Number of records to process per batch. Controls how many records are sent\nto NetSuite in a single API call. Typical values: 50-200.\n"},"internalIdLookup":{"type":"object","description":"Configuration for looking up existing NetSuite records to match against incoming data.\nRequired when operation is \"update\", \"addupdate\", or \"delete\".","properties":{"extract":{"type":"string","description":"Path in the source record to extract the lookup value from (e.g. \"internalId\" or \"externalId\")."},"searchField":{"type":"string","description":"NetSuite field to search against (e.g. \"externalId\", \"email\", \"name\", \"tranId\")."},"operator":{"type":"string","description":"Comparison operator for the lookup (e.g. \"is\", \"contains\", \"startswith\")."},"expression":{"type":"string","description":"NetSuite search expression for complex lookup conditions, used for multi-field or conditional lookups."}}},"hooks":{"type":"object","description":"Script hooks for custom processing at different stages of the import.\nEach hook references a SuiteScript file and function.\n","properties":{"preMap":{"type":"object","description":"Runs before field mapping is applied.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}},"postMap":{"type":"object","description":"Runs after field mapping, before submission to NetSuite.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}},"postSubmit":{"type":"object","description":"Runs after the record is submitted to NetSuite.","properties":{"fileInternalId":{"type":["string","null"],"description":"NetSuite internal ID of the SuiteScript file. Null on hook stubs where no SuiteScript file has been selected."},"function":{"type":"string","description":"Name of the function to execute."},"configuration":{"type":["object","null"],"description":"Configuration object passed to the hook function. Null when the hook carries no static parameters."}}}}},"mapping":{"type":"object","description":"NetSuite-specific Mapper 1.0 field mappings (the \"DA\" dialect).\nUsed by `NetSuiteDistributedImport` exclusively — NetSuite's\nDistributed Adaptor cannot consume generic v1 mappings or the\nMapper 2.0 shape because it needs sublist, subrecord, and\nconditional lookup semantics the generic schemas don't model.\n\nField-level shape: `fields` for body columns, `lists` for\nsublists / line-item arrays.","properties":{"fields":{"type":"array","items":{"type":"object","properties":{"extract":{"type":["string","null"],"description":"Path in the source record to extract the value from.\nUse dot notation for nested fields (e.g., \"address.city\").\nThe platform stores `null` here on fields driven by\n`hardCodedValue` instead of an extract path.\n"},"generate":{"type":"string","description":"The NetSuite field ID to write the value to (e.g., \"companyname\", \"email\", \"subsidiary\").\n"},"hardCodedValue":{"type":"string","description":"A static value to always use instead of extracting from source data.\nMutually exclusive with extract.\n"},"lookupName":{"type":"string","description":"Reference to a lookup defined in netsuite_da.lookups by name."},"dataType":{"type":"string","description":"Data type hint for the field value (e.g., \"string\", \"number\", \"date\", \"boolean\").\n"},"internalId":{"type":"boolean","description":"When true, the value is a NetSuite internal ID reference."},"immutable":{"type":"boolean","description":"When true, this field is only set on record creation, not on updates."},"discardIfEmpty":{"type":"boolean","description":"When true, skip this field mapping if the extracted value is empty."},"extractDateFormat":{"type":"string","description":"Date format of the extracted value (e.g., \"MM/DD/YYYY\", \"ISO8601\").\nUsed to parse date strings from source data.\n"},"extractDateTimezone":{"type":["string","null"],"description":"Timezone of the extracted date value (e.g., \"America/New_York\"). Stored as `null` when not configured."},"subRecordMapping":{"type":"object","description":"Nested Mapper 1.0 mapping for a NetSuite subrecord\nembedded in this field.  A subrecord is a child\nrecord that only exists inside a parent (e.g. the\nInventory Detail on a serialized-item line of a\nsales order); NetSuite reaches it through the\nparent.\n\n**Recursive structure.**  The nested `mapping`\nobject has the same `{fields, lists}` shape as the\ntop-level `netsuite_da.mapping`.  Recursion is\nallowed but rare.","properties":{"recordType":{"type":"string","description":"NetSuite's internal record-type id of the\nSUBRECORD (not the parent).  E.g.\n`inventorydetail`, `itempricing`,\n`mainaddress`.\n"},"jsonPath":{"type":"string","description":"JSONPath into the source record that selects the\ndata feeding this subrecord.  Semantics depend\non placement:\n* Line-level (on `lists[].fields[].`\n  `subRecordMapping`) — often `$` to reuse\n  the enclosing sublist line's row, or a path\n  into a nested array on that row.\n* Body-level (on `fields[].subRecordMapping`)\n  — a path into the source record root.\nDefaults to `$`.\n"},"mapping":{"type":"object","description":"Nested `{fields, lists}` mapping for the\nsubrecord.  Identical shape to the top-level\n`netsuite_da.mapping`.  Subrecords often have\nempty `fields` and only a sublist (e.g.\n`inventorydetail` → `inventoryassignment`).\n"},"lookups":{"type":"array","description":"Subrecord-scoped lookups.  Same shape as the\ntop-level `netsuite_da.lookups`.  Fields inside\nthis `subRecordMapping.mapping` reference them\nvia `lookupName`.  Often `[]`.\n"},"referenceFieldId":{"type":"string","description":"Secondary reference-field identifier used on a\nsmall fraction of subrecord blocks in production\n(~3%).  Exact semantics are not publicly\ndocumented by Celigo — match the value on\nexisting resources when present; do NOT\nsynthesize one.  Leave unset unless a template\nclearly requires it.\n"}}},"conditional":{"type":"object","description":"Conditional logic for when to apply this field mapping\n(\"Only perform mapping when\" in the UI). Lookup-based\nconditions evaluate the lookup named by `lookupName`,\nwhich must be defined on this import.\n","properties":{"lookupName":{"type":"string","description":"Lookup to evaluate for the condition. Must exactly match the\n`name` of a lookup defined on this import."},"when":{"type":"string","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","ignore_if_set"],"description":"Condition that controls when this field mapping is applied."}}}}},"description":"Body-level field mappings. Each entry maps a source field to a NetSuite body field.\n"},"lists":{"type":"array","items":{"type":"object","properties":{"generate":{"type":"string","description":"The NetSuite sublist ID (e.g., \"item\" for sales order line items,\n\"addressbook\" for address sublists).\n"},"jsonPath":{"type":"string","description":"LEGACY — do not set. Nominally the JSON path to the array of sublist records, but NetSuite DA ignores a top-level jsonPath here."},"fields":{"type":"array","items":{"type":"object","properties":{"extract":{"type":["string","null"],"description":"Path from the source record root that produces\nthe value for this sublist column. Paths are\nalways anchored at the source record root —\nnever relative to a pre-iterated row. The\nplatform stores `null` here on fields driven by\n`hardCodedValue` instead of an extract path."},"generate":{"type":"string","description":"NetSuite sublist field ID to write to."},"hardCodedValue":{"type":"string","description":"Static value for this sublist field."},"lookupName":{"type":"string","description":"Reference to a lookup by name."},"dataType":{"type":"string","description":"Data type hint for the field value."},"internalId":{"type":"boolean","description":"When true, the value is a NetSuite internal ID reference."},"isKey":{"type":"boolean","description":"When true, this field is a key field for matching existing sublist lines."},"immutable":{"type":"boolean","description":"When true, this field is only set on record creation, not on updates."},"discardIfEmpty":{"type":"boolean","description":"When true, skips this field mapping if the extracted value is empty."},"extractDateFormat":{"type":"string","description":"Date format of the extracted value."},"extractDateTimezone":{"type":["string","null"],"description":"Timezone of the extracted date value. Stored as `null` when not configured."},"subRecordMapping":{"type":"object","description":"Nested Mapper 1.0 mapping for a NetSuite\nsubrecord embedded in this sublist-field.  A\nsubrecord is a child record that only exists\ninside a parent (e.g. the Inventory Detail on\na serialized-item line of a sales order);\nNetSuite reaches it through the parent.\n\n**Recursive structure.**  The nested\n`mapping` object has the same\n`{fields, lists}` shape as the top-level\n`netsuite_da.mapping`.  Recursion is allowed\nbut rare.","properties":{"recordType":{"type":"string","description":"NetSuite's internal record-type id of the\nSUBRECORD (not the parent).  E.g.\n`inventorydetail`, `itempricing`,\n`componentinventorydetail`.\n"},"jsonPath":{"type":"string","description":"JSONPath (relative to the current sublist\nline) to the rows that populate the\nsubrecord.  Typically `$` when the\nsubrecord data is carried on the same\nline item (the enclosing\n`lists[].jsonPath` has already selected\nthe line).  Defaults to `$`.\n"},"mapping":{"type":"object","description":"Nested `{fields, lists}` mapping for the\nsubrecord.  Identical shape to the\ntop-level `netsuite_da.mapping`.\nSubrecords often have empty `fields` and\nonly a sublist (e.g. `inventorydetail` →\n`inventoryassignment`).\n"},"lookups":{"type":"array","description":"Subrecord-scoped lookups.  Same shape as\nthe top-level `netsuite_da.lookups`.\nFields inside this\n`subRecordMapping.mapping` reference\nthem via `lookupName`.  Often `[]`.\n"},"referenceFieldId":{"type":"string","description":"Secondary reference-field identifier used\non a small fraction of subrecord blocks\nin production (~3%).  Exact semantics are\nnot publicly documented by Celigo — match\nthe value on existing resources when\npresent; do NOT synthesize one.  Leave\nunset unless a template clearly requires\nit.\n"}}},"conditional":{"type":"object","description":"Conditional logic for when to apply this sublist field mapping\n(\"Only perform mapping when\" in the UI). Lookup-based conditions\nevaluate the lookup named by `lookupName`, which must be defined\non this import.","properties":{"lookupName":{"type":"string","description":"Lookup to evaluate for the condition. Must exactly match the\n`name` of a lookup defined on this import."},"when":{"type":"string","description":"Condition that controls when this field mapping is applied.","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","ignore_if_set"]}}}}},"description":"Field mappings for each column in the sublist."}}},"description":"Sublist (line-item) mappings. Each entry maps source data to a NetSuite sublist.\n"}}},"lookups":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this lookup. Mapping fields reference it\nvia `lookupName` and Handlebars expressions reference it via\n`{{lookup \"name\" value}}`. Must be unique within this\nresource's `lookups` array."},"recordType":{"type":"string","description":"NetSuite record type to search. Use the internal record-type\nid (lowercase, underscored), e.g. `customer`, `item`,\n`salesorder`, `customlist_xxx`."},"searchField":{"type":"string","description":"NetSuite field on `recordType` to match against. Use the\ninternal field id — e.g. `email`, `externalid`,\n`internalid`, `entityid`, `custitem_xxx`."},"resultField":{"type":"string","description":"NetSuite field whose value is returned when a record matches.\nTypically `internalid` when the lookup resolves to a record\nreference, or any queryable scalar field."},"expression":{"type":"string","description":"Optional advanced-search expression (NetSuite saved-search\nDSL) for multi-criterion lookups. When present, supersedes\n`searchField` / `operator` — those fields are ignored."},"operator":{"type":"string","description":"Comparison operator for the `searchField` match. Common\nvalues: `is`, `contains`, `startswith`, `anyof`,\n`equalto`, `greaterthan`."},"includeInactive":{"type":"boolean","description":"When true, includes inactive records in lookup results."},"useDefaultOnMultipleMatches":{"type":"boolean","description":"Controls behaviour when the search matches more than one\nrecord. When true, the `default` value is returned instead\nof raising an ambiguous-match error. When false (default),\nmultiple matches are treated as a failure."},"allowFailures":{"type":["boolean","null"],"description":"When true, a lookup miss (no matching record and no\n`default`) resolves to `null` and the import continues.\nWhen false (default), a miss halts the record. May be\nstored as null (treated as unset)."},"map":{"type":["object","null"],"description":"Optional static key→value object evaluated BEFORE the\nNetSuite search. When the incoming value matches a key, the\nmapped value is returned without hitting NetSuite. Null on\ndynamic (search-based) lookups with no static map."},"default":{"type":["string","null"],"description":"Value returned when the search matches no records. When\nomitted and `allowFailures` is false, an unmatched lookup\nhalts the record. May be stored as null (no default)."}}},"description":"Lookup definitions that resolve reference values from NetSuite.\nReferenced by name from field mappings via `lookupName` and from\nHandlebars expressions via `{{lookup \"name\" value}}`."},"rawOverride":{"type":"object","description":"Raw override object for advanced use cases. When useRawOverride is true,\nthis object is sent directly to the NetSuite API, bypassing normal mapping.\n"},"useRawOverride":{"type":"boolean","description":"When true, uses rawOverride instead of the normal mapping configuration.\n"},"isMigrated":{"type":"boolean","description":"When true, this import was migrated from a legacy format. Set by the system."},"retryUpdateAsAdd":{"type":"boolean","description":"When true, if an update fails because the record doesn't exist, automatically\nretry as an add operation. Useful for initial syncs where records may not exist yet.\n"},"customFieldMetadata":{"type":"object","description":"Metadata about custom fields on the target record type.\nPopulated by the system from NetSuite metadata — do not set manually.\n"},"file":{"type":"object","description":"File cabinet configuration for file-based imports into NetSuite.\n","properties":{"name":{"type":"string","description":"Filename for the file in NetSuite File Cabinet."},"fileType":{"type":"string","description":"NetSuite file type (e.g., \"PDF\", \"CSV\", \"PLAINTEXT\", \"EXCEL\", \"XML\").\n"},"folder":{"type":"string","description":"Folder path or name in the NetSuite File Cabinet."},"folderInternalId":{"type":"string","description":"Internal ID of the target folder in NetSuite File Cabinet."},"internalId":{"type":"string","description":"Internal ID of an existing file to update."},"backupFolderInternalId":{"type":"string","description":"Internal ID of a backup folder for file versioning."}}},"isFileProvider":{"type":"boolean","description":"Whether this import handles files in the NetSuite File Cabinet.\n"},"preferences":{"type":"object","properties":{"ignoreReadOnlyFields":{"type":"boolean","description":"When true, silently skip read-only fields instead of raising errors."},"warningAsError":{"type":"boolean","description":"When true, treat NetSuite warnings as errors that stop the import."},"skipCustomMetadataRequests":{"type":"boolean","description":"When true, skip fetching custom field metadata to improve performance."}},"description":"Import behavior preferences that control how NetSuite handles the import operation.\n"},"recordTypeId":{"type":"string","description":"The internal record type ID. Used for custom record types in NetSuite where\nthe numeric ID is needed in addition to the recordType string.\n"}},"if":{"not":{"propertyNames":{"enum":["lookups","mapping","missingOrCorruptedDAConfig"]}}},"then":{"required":["operation","recordType"],"if":{"properties":{"operation":{"enum":["update","addupdate","delete"]}},"required":["operation"]},"then":{"required":["internalIdLookup"]}}},"MappingField":{"type":"object","description":"One Mapper 1.0 field-mapping entry.","properties":{"generate":{"type":"string","description":"Target field path to write on the destination record."},"extract":{"type":"string","description":"Source expression. Accepts:\n- JSONPath starting with `$.` (e.g. `$.customer.firstName`).\n- Handlebars template (contains `{{`) for concatenation /\n  formatting / conditional logic (e.g.\n  `{{record.firstName}} {{record.lastName}}`).\n- A literal string (any value not starting with `$.` and not\n  containing `{{`) — treated as a hardcoded value.\n\nOmit when using `hardCodedValue`.\n"},"hardCodedValue":{"type":["string","null"],"description":"Static value written to `generate` instead of extracting from the source record."},"dataType":{"type":"string","enum":["string","number","boolean","numberarray","stringarray","json"],"description":"Data type coercion applied to the mapped value."},"discardIfEmpty":{"type":"boolean","description":"When true, the field is omitted from the output when the extracted value is empty."},"immutable":{"type":"boolean","description":"When true, the mapped value cannot be overwritten by later mapping steps."},"lookupName":{"type":"string","description":"Name of an entry in the import's `lookups` array used to translate the value."},"default":{"type":["string","null"],"description":"Fallback value used when the extract yields no value."},"extractDateFormat":{"type":"string","description":"Date format of the source value, used to parse it before conversion."},"extractDateTimezone":{"type":"string","description":"Timezone applied when parsing the source date value."},"generateDateFormat":{"type":"string","description":"Date format applied to the value written to the destination."},"generateDateTimezone":{"type":"string","description":"Timezone applied when formatting the destination date value."},"conditional":{"type":"object","description":"Only apply this mapping entry when the given condition is satisfied.\n","properties":{"when":{"type":"string","enum":["record_created","record_updated","extract_not_empty","lookup_not_empty","lookup_empty","expression"],"description":"Condition that gates whether this mapping entry is applied.\n`lookup_not_empty` / `lookup_empty` evaluate the lookup named by the\nsibling `lookupName`; `expression` evaluates the sibling `expression`.\n"},"lookupName":{"type":"string","description":"Lookup to evaluate for the `lookup_not_empty` / `lookup_empty`\nconditions.\n"},"expression":{"type":"string","description":"Expression evaluated when `when` is `expression`.\n"}}}}},"MappingPreviewResponse":{"type":"object","description":"Result of resolving a NetSuite import's field mappings against the supplied\nsample records. The mapped NetSuite fields appear under\n`data.returnedObjects.jsObjects.data[].data.nlobjFieldIds`; mapping failures\nsurface in `data.returnedObjects.mappingErrors`. No record is written to\nNetSuite.","required":["success","data"],"properties":{"success":{"type":"boolean","description":"When true, the mapping engine processed the request. Per-record mapping problems still surface in `mappingErrors`, so inspect that before trusting the mapped output."},"data":{"type":"object","properties":{"returnedObjects":{"type":"object","properties":{"jsObjects":{"type":"object","description":"The mapped records produced by the engine.","properties":{"data":{"type":"array","description":"One entry per input record, in input order.","items":{"type":"object","properties":{"celigoIsElement":{"type":"boolean","description":"Internal marker indicating the entry is a mapped record element."},"data":{"type":"object","description":"The mapped NetSuite record for this input record.","properties":{"nlobjFieldIds":{"type":"object","additionalProperties":true,"description":"Resolved body-field values keyed by the NetSuite field id\n(the `generate` target of each mapping). Handlebars\nexpressions in the mapping are already evaluated here."},"nlobjSublistIds":{"type":"object","additionalProperties":true,"description":"Resolved sublist (line-item) values keyed by NetSuite sublist id. Empty when the mapping defines no `lists`."}}}}}},"originalSize":{"type":"integer","description":"Number of input records received."},"processedCount":{"type":"integer","description":"Number of records processed by the engine."}}},"mappingErrors":{"type":"array","description":"Mapping failures encountered while resolving fields. Empty when every field resolved cleanly.","items":{"type":"object","additionalProperties":true}},"celigo_classname":{"type":"string","description":"Internal engine class name for the result (e.g. `Celigo.integrator.mapping.model.MapToNetSuiteResult`)."}}}}}}},"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/netsuiteDA/previewImportMappingFields":{"put":{"operationId":"previewImportMappingFields","tags":["Imports"],"summary":"Preview NetSuite import field mappings against sample data","description":"Resolves a NetSuite import's Mapper 1.0 field mappings against supplied\nsample records and returns the mapped NetSuite record — the body fields\n(`nlobjFieldIds`) and sublists (`nlobjSublistIds`) that *would* be sent —\nwithout writing anything to NetSuite. Handlebars expressions in the mapping\nare evaluated, so this is the way to confirm how a mapping resolves before\nrunning it live.\n\nThis is a stateless dry-run of the mapping engine: no Job is created and no\nNetSuite record is touched. It is the NetSuite-mapping counterpart to\n`POST /v1/imports/preview`, which previews the full import pipeline; use this\noperation when you specifically want the resolved NetSuite field values.","parameters":[{"name":"_connectionId","in":"query","required":true,"description":"The NetSuite connection whose Distributed Adaptor configuration resolves the mapping.","schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MappingPreviewRequest"}}}},"responses":{"200":{"description":"Mapping resolved. The mapped NetSuite fields are under\n`data.returnedObjects.jsObjects.data[].data.nlobjFieldIds`. Inspect\n`data.returnedObjects.mappingErrors` before trusting the output — it\nlists any fields that failed to resolve.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MappingPreviewResponse"}}}},"400":{"description":"A required input is missing — the `_connectionId` query parameter or the\n`importConfig` body field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"}}}}}}
```

## Preview import data

> Runs the import pipeline — field mappings, transformations, and hooks —\
> against the supplied sample data and returns the per-record results.\
> Treat the preview as capable of executing the composed destination\
> request: its unscoped sibling (\`POST /v1/imports/preview\`) is verified\
> to write real records to live destinations and offers no option to\
> disable the send, so do not preview create/update imports against\
> production systems with data you do not want written. For a\
> compose-only preview of the destination request, use\
> \`POST /v1/pageProcessors/preview\` with \`{preview: true}\` on the\
> target entry.<br>

```json
{"openapi":"3.2.0","info":{"title":"Imports","version":"1.0.0"},"tags":[{"name":"Imports","description":"Imports deliver data to destination systems. An import receives pages of records from\nupstream flow steps, applies field mappings and transformations, and writes the results\nto the target application, database, or file destination — handling lookups, duplicate\navoidance, and per-record error reporting along the way. Each import uses one\n`adaptorType` that determines its connection compatibility and configuration object.\n\n## Import schema\n\n{% openapi-schemas spec=\"import\" schemas=\"Import\" 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 running sample data through an import's pipeline\n(mappings, transformations, hooks). The preview can execute the\ncomposed destination request — see the operation descriptions for the\nwrite-safety warning.\n","properties":{"_importId":{"type":"string","format":"objectId","description":"The import to preview. Required when previewing within a flow."},"sampleData":{"description":"Sample source record(s) to run through the import's mappings and transformations.\nMay be a single object or an array of objects depending on the adaptor.\n","oneOf":[{"title":"Single record","type":"object","additionalProperties":true},{"title":"Array of records","type":"array","items":{"type":"object","additionalProperties":true}}]},"options":{"type":"object","description":"Optional overrides applied only for this preview.","additionalProperties":true}},"additionalProperties":true},"PreviewResponse":{"type":"object","description":"Result of running a preview against the configured import. Shows the\nmapped/transformed records as composed for the destination.\n","properties":{"data":{"type":"array","description":"Mapped and transformed records produced by the import.","items":{"type":"object","additionalProperties":true}},"errors":{"type":"array","description":"Any validation or transformation errors encountered while building the preview.","items":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable code identifying the type of preview error."},"message":{"type":"string","description":"Human-readable explanation of what went wrong while building the preview."},"path":{"type":"string","description":"Path to the field or configuration element the error relates to."}}}}},"additionalProperties":true},"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}/imports/preview":{"post":{"summary":"Preview import data","description":"Runs the import pipeline — field mappings, transformations, and hooks —\nagainst the supplied sample data and returns the per-record results.\nTreat the preview as capable of executing the composed destination\nrequest: its unscoped sibling (`POST /v1/imports/preview`) is verified\nto write real records to live destinations and offers no option to\ndisable the send, so do not preview create/update imports against\nproduction systems with data you do not want written. For a\ncompose-only preview of the destination request, use\n`POST /v1/pageProcessors/preview` with `{preview: true}` on the\ntarget entry.\n","operationId":"previewImportData","tags":["Imports"],"parameters":[{"in":"path","name":"_integrationId","required":true,"schema":{"type":"string","format":"objectId"},"description":"Integration ID"},{"in":"path","name":"_flowId","required":true,"schema":{"type":"string","format":"objectId"},"description":"Flow ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewRequest"}}}},"responses":{"200":{"description":"Successfully previewed import 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"}}}}}}
```

## List dependencies of an import

> 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":"Imports","version":"1.0.0"},"tags":[{"name":"Imports","description":"Imports deliver data to destination systems. An import receives pages of records from\nupstream flow steps, applies field mappings and transformations, and writes the results\nto the target application, database, or file destination — handling lookups, duplicate\navoidance, and per-record error reporting along the way. Each import uses one\n`adaptorType` that determines its connection compatibility and configuration object.\n\n## Import schema\n\n{% openapi-schemas spec=\"import\" schemas=\"Import\" 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."},"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/imports/{_id}/dependencies":{"get":{"operationId":"listImportDependencies","tags":["Imports"],"summary":"List dependencies of an import","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"}}}}}}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developer.celigo.com/api/api-reference/imports.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
