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

# Tools

Tools are reusable processing units within integrations that encapsulate input transformation, conditional routing, output mapping, and data enrichment logic behind an input/output contract. They can be referenced from flows, APIs, AI agents, MCP servers, and other tools to promote modularity and reuse.

### Tool schema

## The Tool object

````json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"components":{"schemas":{"Tool":{"type":"object","required":["_id","name","_integrationId","createdAt","lastModified"],"description":"Tool object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ToolBase"},{"$ref":"#/components/schemas/ResourceResponse"},{"type":"object","properties":{"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Origin resource ID when this tool was created by cloning or installing a template."},"draftExpiresAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when a draft tool auto-deletes. Server-computed when `draft` is set at\ncreation."}}}]},"ToolBase":{"type":"object","description":"Writable tool fields shared by the request and response schemas.","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Human-readable name for the tool.\n\nDisplayed in the UI and used to identify the tool's purpose.\n"},"description":{"type":"string","maxLength":5120,"description":"Optional detailed description of what the tool does.\n\nUse this to document the tool's purpose, expected inputs/outputs,\nand any special considerations.\n"},"_integrationId":{"type":"string","format":"objectId","description":"Reference to the integration this tool belongs to.\n\nEvery tool must be associated with an integration. The integration\ndetermines the scope and access controls for the tool.\n"},"input":{"$ref":"#/components/schemas/Input"},"output":{"$ref":"#/components/schemas/Output"},"routers":{"type":"array","description":"Optional routers for conditional processing logic.\n\nRouters allow you to direct input data to different processing branches\nbased on filter criteria or script logic. Tools only support\n\"first_matching_branch\" routing strategy.\n\nBranches can chain to other routers or use the special \"outputRouter\"\nterminal value to exit the tool.\n","items":{"$ref":"#/components/schemas/Router"}},"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"draft":{"type":"boolean","description":"When true, this tool is a draft that auto-deletes when its expiry passes\n(`draftExpiresAt` in the response). Set at creation; an update can clear the\nflag but never set it."}}},"Input":{"type":"object","description":"Configuration for the tool's input processing.\n\nDefines the expected input structure, optional transformations to apply\nbefore routing, and mock data for testing.\n","properties":{"name":{"type":"string","maxLength":200,"description":"Display name for the input configuration.\n"},"description":{"type":"string","maxLength":10240,"description":"Description of the expected input data and its purpose.\n"},"schema":{"type":"object","description":"JSON Schema describing the expected input data structure.\n\nUsed for validation, documentation, and AI-assisted tooling.\nMust be a valid JSON Schema document.\n","additionalProperties":true},"transform":{"$ref":"#/components/schemas/Transform"},"debugUntil":{"type":"string","format":"date-time","description":"Enables debug capture for the tool input stage until this timestamp.\nWhile it is in the future, invocations write input-stage\nrequest/response logs, listed at\n`GET /v1/tools/{_id}/tool_input/requests`. At most 1 hour in the\nfuture. A past timestamp (or omitting the field) stops capture.\nArm/disarm with `PATCH /v1/tools/{_id}` path `/input/debugUntil`.\n"},"mockInput":{"type":"object","description":"Mock data for testing the tool's input processing.\n\nProvides sample input to test transformation logic and routing\nwithout requiring live data. Maximum size: 1MB.\n","additionalProperties":true}}},"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"]},"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"}}}},"Output":{"type":"object","description":"Configuration for the tool's output processing.\n\nDefines how the tool's results are mapped, transformed, and enriched\nbefore being returned. Supports field mappings, lookups for data\nenrichment, and custom script hooks for pre/post-mapping processing.\n","properties":{"name":{"type":"string","maxLength":200,"description":"Display name for the output configuration.\n"},"description":{"type":"string","maxLength":10240,"description":"Description of the output data and its purpose.\n"},"schema":{"type":"object","description":"JSON Schema describing the output data structure.\n\nUsed for documentation and validation of the tool's output.\nMust be a valid JSON Schema document.\n","additionalProperties":true},"mappings":{"description":"Field mappings to transform data into the output format.\n\nMaps data from processing results to the output structure.\nUses Celigo's standard mapping format with extract/generate field paths —\na flat array of mapping entries (each entry may recurse via its own\nnested ``mappings`` for object/array structures).\n","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"lookups":{"type":"array","description":"Lookup tables for data enrichment during output processing.\n\nStatic key-value mappings used to translate values (e.g., status codes,\ncategory names) during output generation.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the lookup, used to reference it from mappings.\n"},"map":{"type":"object","description":"Key-value mapping object. Keys are the input values and\nvalues are the corresponding output values.\n","additionalProperties":true},"default":{"type":"string","description":"Default value returned when the input key is not found in the map.\n"},"allowFailures":{"type":"boolean","description":"Whether to continue processing if the lookup fails to find a match\nand no default is provided.\n"}}}},"hooks":{"type":"object","description":"Custom script hooks for pre- and post-mapping processing.\n\nAllows running custom JavaScript functions before and after\noutput mappings are applied.\n","properties":{"preMap":{"type":"object","description":"Script to run before applying output mappings.\n\nCan modify the data before it is mapped to the output structure.\n","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name to execute within the script"}}},"postMap":{"type":"object","description":"Script to run after applying output mappings.\n\nCan modify the final output data after mappings are applied.\n","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name to execute within the script"}}}}},"debugUntil":{"type":"string","format":"date-time","description":"Enables debug capture for the tool output stage until this timestamp.\nWhile it is in the future, invocations write output-stage\nrequest/response logs, listed at\n`GET /v1/tools/{_id}/tool_output/requests`. At most 1 hour in the\nfuture. A past timestamp (or omitting the field) stops capture.\nArm/disarm with `PATCH /v1/tools/{_id}` path `/output/debugUntil`.\n"},"mockInput":{"type":"object","description":"Mock data for testing the tool's output processing.\n\nProvides sample data that would arrive from the routing/processing\nstage, used to test mapping and lookup logic. Maximum size: 1MB.\n","additionalProperties":true}}},"Router":{"type":"object","description":"Configuration for conditional routing within a tool.\n\nRouters evaluate input data and direct it to different processing branches\nbased on criteria. This enables complex business logic and conditional\nprocessing within the tool.\n\nUnlike flows, tools only support \"first_matching_branch\" routing strategy.\nBranches can chain to other routers or use the special \"outputRouter\"\nterminal sink to exit the tool and return results.\n","properties":{"id":{"type":"string","description":"Unique identifier for this router within the tool.\n\nUsed to reference this router from other routers' branch `nextRouterId`.\n"},"name":{"type":"string","maxLength":300,"description":"Human-readable name for the router.\n"},"routeRecordsTo":{"type":"string","enum":["first_matching_branch"],"description":"Routing strategy. Tools only support \"first_matching_branch\",\nwhich routes to the first branch whose criteria match the input.\n"},"routeRecordsUsing":{"type":"string","enum":["input_filters","script"],"description":"Method used to evaluate routing criteria.\n\n- **input_filters**: Use declarative filter expressions on each branch\n- **script**: Use a custom JavaScript function to determine the branch\n"},"script":{"type":"object","description":"Script configuration when routeRecordsUsing is \"script\".\n\nThe function should return the name of the branch to route to.\n","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name that returns the branch name"}}},"branches":{"type":"array","description":"List of branches defining different processing paths.\n\nEach branch has optional filter criteria and a set of processing steps.\nRecords are evaluated against branch criteria in order; the first\nmatching branch is selected.\n","items":{"type":"object","properties":{"name":{"type":"string","maxLength":300,"description":"Name of this branch.\n"},"description":{"type":"string","maxLength":10240,"description":"Description of when and why this branch is selected.\n"},"branchId":{"type":"string","description":"Stable identifier for this branch within the tool, generated by\nthe builder. Used to reference the branch independently of its\nposition in the branches array (e.g., from step requests).\n"},"inputFilter":{"type":"object","description":"Filter criteria to determine if this branch should be selected.\n\nUses Celigo's expression-based filter format.\n","properties":{"version":{"type":"string","enum":["1"],"description":"Filter version"},"rules":{"type":"array","description":"Filter rules in Celigo expression-based filter format.\n\nArray-based DSL where the first element is an operator (e.g., \"equals\", \"and\", \"or\"),\nfollowed by operands which can be nested expressions.\n","items":{}}}},"nextRouterId":{"type":"string","description":"Identifier of the next router to chain to after this branch completes.\n\nUse \"outputRouter\" as a special terminal value to exit the tool\nand return the processing results.\n"},"pageProcessors":{"type":"array","description":"Processing steps to execute in this branch.\n\nEach processor references an export (lookup) or import resource\nfor data retrieval or submission.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["export","import"],"description":"Type of processor.\n\n- **export**: Retrieves data from an external system (lookup)\n- **import**: Sends data to an external system\n"},"_exportId":{"type":"string","format":"objectId","description":"Export resource reference (when type is \"export\")"},"_importId":{"type":"string","format":"objectId","description":"Import resource reference (when type is \"import\")"},"proceedOnFailure":{"type":"boolean","description":"Whether to continue processing subsequent steps if this\nprocessor fails.\n"},"setupInProgress":{"type":"boolean","description":"When true, the processor's configuration is still being\nset up in the UI and the step is not yet runnable.\n"},"responseMapping":{"type":"object","description":"Merges fields from this processor's response back onto the\nin-flight record so later processors and the tool's output\ncan read them. Extracts do NOT read the raw application\nresponse — they evaluate against the platform's canonical\nper-record envelope: for lookups (`type: \"export\"`) that is\n`{\"statusCode\", \"data\": [<result records>], \"errors\"}`, so\npaths must start from `data` (e.g. `data[0].name`); for\nimports it is `{\"id\", \"statusCode\", \"ignored\", \"_json\"}`,\nso use `id` or `_json.<path>`. Bare result-record field\nnames resolve to nothing and merge nothing.\n","properties":{"fields":{"type":"array","description":"Simple field-level mappings","items":{"type":"object","properties":{"extract":{"type":"string","description":"Path within the canonical response envelope to\ncopy the value from (`data[0].x` / `data.0.x`\nfor lookups; `id` or `_json.<path>` for\nimports).\n"},"generate":{"type":"string","description":"Field path on the in-flight record where the\nextracted value is stored (dot notation for\nnesting).\n"}}}},"lists":{"type":"array","description":"List-level mappings for array data","items":{"type":"object","properties":{"generate":{"type":"string","description":"Target list path"},"fields":{"type":"array","description":"Field-level mappings applied to each item in the list.","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path"},"generate":{"type":"string","description":"Target field path"}}}}}}}}},"hooks":{"type":"object","description":"Custom scripts for processing","properties":{"postResponseMap":{"type":"object","description":"Script to run after response mapping","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name to execute"}}}}}}}}}}}}},"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"}}},"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"]}}}}
````

## List tools

> Returns all tools in the account. Filter by \`\_integrationId\` to scope\
> results to a single integration.

````json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"tags":[{"name":"Tools","description":"Tools are reusable processing units within integrations that encapsulate input\ntransformation, conditional routing, output mapping, and data enrichment logic behind\nan input/output contract. They can be referenced from flows, APIs, AI agents, MCP\nservers, and other tools to promote modularity and reuse.\n\n## Tool schema\n\n{% openapi-schemas spec=\"tool\" schemas=\"Tool\" 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":{"Tool":{"type":"object","required":["_id","name","_integrationId","createdAt","lastModified"],"description":"Tool object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ToolBase"},{"$ref":"#/components/schemas/ResourceResponse"},{"type":"object","properties":{"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Origin resource ID when this tool was created by cloning or installing a template."},"draftExpiresAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when a draft tool auto-deletes. Server-computed when `draft` is set at\ncreation."}}}]},"ToolBase":{"type":"object","description":"Writable tool fields shared by the request and response schemas.","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Human-readable name for the tool.\n\nDisplayed in the UI and used to identify the tool's purpose.\n"},"description":{"type":"string","maxLength":5120,"description":"Optional detailed description of what the tool does.\n\nUse this to document the tool's purpose, expected inputs/outputs,\nand any special considerations.\n"},"_integrationId":{"type":"string","format":"objectId","description":"Reference to the integration this tool belongs to.\n\nEvery tool must be associated with an integration. The integration\ndetermines the scope and access controls for the tool.\n"},"input":{"$ref":"#/components/schemas/Input"},"output":{"$ref":"#/components/schemas/Output"},"routers":{"type":"array","description":"Optional routers for conditional processing logic.\n\nRouters allow you to direct input data to different processing branches\nbased on filter criteria or script logic. Tools only support\n\"first_matching_branch\" routing strategy.\n\nBranches can chain to other routers or use the special \"outputRouter\"\nterminal value to exit the tool.\n","items":{"$ref":"#/components/schemas/Router"}},"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"draft":{"type":"boolean","description":"When true, this tool is a draft that auto-deletes when its expiry passes\n(`draftExpiresAt` in the response). Set at creation; an update can clear the\nflag but never set it."}}},"Input":{"type":"object","description":"Configuration for the tool's input processing.\n\nDefines the expected input structure, optional transformations to apply\nbefore routing, and mock data for testing.\n","properties":{"name":{"type":"string","maxLength":200,"description":"Display name for the input configuration.\n"},"description":{"type":"string","maxLength":10240,"description":"Description of the expected input data and its purpose.\n"},"schema":{"type":"object","description":"JSON Schema describing the expected input data structure.\n\nUsed for validation, documentation, and AI-assisted tooling.\nMust be a valid JSON Schema document.\n","additionalProperties":true},"transform":{"$ref":"#/components/schemas/Transform"},"debugUntil":{"type":"string","format":"date-time","description":"Enables debug capture for the tool input stage until this timestamp.\nWhile it is in the future, invocations write input-stage\nrequest/response logs, listed at\n`GET /v1/tools/{_id}/tool_input/requests`. At most 1 hour in the\nfuture. A past timestamp (or omitting the field) stops capture.\nArm/disarm with `PATCH /v1/tools/{_id}` path `/input/debugUntil`.\n"},"mockInput":{"type":"object","description":"Mock data for testing the tool's input processing.\n\nProvides sample input to test transformation logic and routing\nwithout requiring live data. Maximum size: 1MB.\n","additionalProperties":true}}},"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"]},"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"}}}},"Output":{"type":"object","description":"Configuration for the tool's output processing.\n\nDefines how the tool's results are mapped, transformed, and enriched\nbefore being returned. Supports field mappings, lookups for data\nenrichment, and custom script hooks for pre/post-mapping processing.\n","properties":{"name":{"type":"string","maxLength":200,"description":"Display name for the output configuration.\n"},"description":{"type":"string","maxLength":10240,"description":"Description of the output data and its purpose.\n"},"schema":{"type":"object","description":"JSON Schema describing the output data structure.\n\nUsed for documentation and validation of the tool's output.\nMust be a valid JSON Schema document.\n","additionalProperties":true},"mappings":{"description":"Field mappings to transform data into the output format.\n\nMaps data from processing results to the output structure.\nUses Celigo's standard mapping format with extract/generate field paths —\na flat array of mapping entries (each entry may recurse via its own\nnested ``mappings`` for object/array structures).\n","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"lookups":{"type":"array","description":"Lookup tables for data enrichment during output processing.\n\nStatic key-value mappings used to translate values (e.g., status codes,\ncategory names) during output generation.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the lookup, used to reference it from mappings.\n"},"map":{"type":"object","description":"Key-value mapping object. Keys are the input values and\nvalues are the corresponding output values.\n","additionalProperties":true},"default":{"type":"string","description":"Default value returned when the input key is not found in the map.\n"},"allowFailures":{"type":"boolean","description":"Whether to continue processing if the lookup fails to find a match\nand no default is provided.\n"}}}},"hooks":{"type":"object","description":"Custom script hooks for pre- and post-mapping processing.\n\nAllows running custom JavaScript functions before and after\noutput mappings are applied.\n","properties":{"preMap":{"type":"object","description":"Script to run before applying output mappings.\n\nCan modify the data before it is mapped to the output structure.\n","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name to execute within the script"}}},"postMap":{"type":"object","description":"Script to run after applying output mappings.\n\nCan modify the final output data after mappings are applied.\n","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name to execute within the script"}}}}},"debugUntil":{"type":"string","format":"date-time","description":"Enables debug capture for the tool output stage until this timestamp.\nWhile it is in the future, invocations write output-stage\nrequest/response logs, listed at\n`GET /v1/tools/{_id}/tool_output/requests`. At most 1 hour in the\nfuture. A past timestamp (or omitting the field) stops capture.\nArm/disarm with `PATCH /v1/tools/{_id}` path `/output/debugUntil`.\n"},"mockInput":{"type":"object","description":"Mock data for testing the tool's output processing.\n\nProvides sample data that would arrive from the routing/processing\nstage, used to test mapping and lookup logic. Maximum size: 1MB.\n","additionalProperties":true}}},"Router":{"type":"object","description":"Configuration for conditional routing within a tool.\n\nRouters evaluate input data and direct it to different processing branches\nbased on criteria. This enables complex business logic and conditional\nprocessing within the tool.\n\nUnlike flows, tools only support \"first_matching_branch\" routing strategy.\nBranches can chain to other routers or use the special \"outputRouter\"\nterminal sink to exit the tool and return results.\n","properties":{"id":{"type":"string","description":"Unique identifier for this router within the tool.\n\nUsed to reference this router from other routers' branch `nextRouterId`.\n"},"name":{"type":"string","maxLength":300,"description":"Human-readable name for the router.\n"},"routeRecordsTo":{"type":"string","enum":["first_matching_branch"],"description":"Routing strategy. Tools only support \"first_matching_branch\",\nwhich routes to the first branch whose criteria match the input.\n"},"routeRecordsUsing":{"type":"string","enum":["input_filters","script"],"description":"Method used to evaluate routing criteria.\n\n- **input_filters**: Use declarative filter expressions on each branch\n- **script**: Use a custom JavaScript function to determine the branch\n"},"script":{"type":"object","description":"Script configuration when routeRecordsUsing is \"script\".\n\nThe function should return the name of the branch to route to.\n","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name that returns the branch name"}}},"branches":{"type":"array","description":"List of branches defining different processing paths.\n\nEach branch has optional filter criteria and a set of processing steps.\nRecords are evaluated against branch criteria in order; the first\nmatching branch is selected.\n","items":{"type":"object","properties":{"name":{"type":"string","maxLength":300,"description":"Name of this branch.\n"},"description":{"type":"string","maxLength":10240,"description":"Description of when and why this branch is selected.\n"},"branchId":{"type":"string","description":"Stable identifier for this branch within the tool, generated by\nthe builder. Used to reference the branch independently of its\nposition in the branches array (e.g., from step requests).\n"},"inputFilter":{"type":"object","description":"Filter criteria to determine if this branch should be selected.\n\nUses Celigo's expression-based filter format.\n","properties":{"version":{"type":"string","enum":["1"],"description":"Filter version"},"rules":{"type":"array","description":"Filter rules in Celigo expression-based filter format.\n\nArray-based DSL where the first element is an operator (e.g., \"equals\", \"and\", \"or\"),\nfollowed by operands which can be nested expressions.\n","items":{}}}},"nextRouterId":{"type":"string","description":"Identifier of the next router to chain to after this branch completes.\n\nUse \"outputRouter\" as a special terminal value to exit the tool\nand return the processing results.\n"},"pageProcessors":{"type":"array","description":"Processing steps to execute in this branch.\n\nEach processor references an export (lookup) or import resource\nfor data retrieval or submission.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["export","import"],"description":"Type of processor.\n\n- **export**: Retrieves data from an external system (lookup)\n- **import**: Sends data to an external system\n"},"_exportId":{"type":"string","format":"objectId","description":"Export resource reference (when type is \"export\")"},"_importId":{"type":"string","format":"objectId","description":"Import resource reference (when type is \"import\")"},"proceedOnFailure":{"type":"boolean","description":"Whether to continue processing subsequent steps if this\nprocessor fails.\n"},"setupInProgress":{"type":"boolean","description":"When true, the processor's configuration is still being\nset up in the UI and the step is not yet runnable.\n"},"responseMapping":{"type":"object","description":"Merges fields from this processor's response back onto the\nin-flight record so later processors and the tool's output\ncan read them. Extracts do NOT read the raw application\nresponse — they evaluate against the platform's canonical\nper-record envelope: for lookups (`type: \"export\"`) that is\n`{\"statusCode\", \"data\": [<result records>], \"errors\"}`, so\npaths must start from `data` (e.g. `data[0].name`); for\nimports it is `{\"id\", \"statusCode\", \"ignored\", \"_json\"}`,\nso use `id` or `_json.<path>`. Bare result-record field\nnames resolve to nothing and merge nothing.\n","properties":{"fields":{"type":"array","description":"Simple field-level mappings","items":{"type":"object","properties":{"extract":{"type":"string","description":"Path within the canonical response envelope to\ncopy the value from (`data[0].x` / `data.0.x`\nfor lookups; `id` or `_json.<path>` for\nimports).\n"},"generate":{"type":"string","description":"Field path on the in-flight record where the\nextracted value is stored (dot notation for\nnesting).\n"}}}},"lists":{"type":"array","description":"List-level mappings for array data","items":{"type":"object","properties":{"generate":{"type":"string","description":"Target list path"},"fields":{"type":"array","description":"Field-level mappings applied to each item in the list.","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path"},"generate":{"type":"string","description":"Target field path"}}}}}}}}},"hooks":{"type":"object","description":"Custom scripts for processing","properties":{"postResponseMap":{"type":"object","description":"Script to run after response mapping","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name to execute"}}}}}}}}}}}}},"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"}}},"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"]}},"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/tools":{"get":{"summary":"List tools","description":"Returns all tools in the account. Filter by `_integrationId` to scope\nresults to a single integration.","operationId":"listTools","tags":["Tools"],"parameters":[{"name":"_integrationId","in":"query","description":"Filter tools by integration identifier","required":false,"schema":{"type":"string"}},{"$ref":"#/components/parameters/Include"},{"$ref":"#/components/parameters/Exclude"}],"responses":{"200":{"description":"Successfully retrieved list of tools","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/Tool"}}}}},"204":{"description":"No tools exist in the account"},"401":{"$ref":"#/components/responses/401-unauthorized"}}}}}}
````

## Create a tool

> Creates a new tool within an integration. \`name\` and \`\_integrationId\`\
> are required. Routers use \`first\_matching\_branch\` strategy only, and each\
> branch's \`nextRouterId\` must point to another router's \`id\` or\
> \`"outputRouter"\` to exit the tool.

````json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"tags":[{"name":"Tools","description":"Tools are reusable processing units within integrations that encapsulate input\ntransformation, conditional routing, output mapping, and data enrichment logic behind\nan input/output contract. They can be referenced from flows, APIs, AI agents, MCP\nservers, and other tools to promote modularity and reuse.\n\n## Tool schema\n\n{% openapi-schemas spec=\"tool\" schemas=\"Tool\" 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":"Request schema for creating or updating a tool. Tools are reusable processing\nunits that encapsulate input transformation, conditional routing, and output\nmapping logic within an integration.","required":["name","_integrationId"],"allOf":[{"$ref":"#/components/schemas/ToolBase"}]},"ToolBase":{"type":"object","description":"Writable tool fields shared by the request and response schemas.","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Human-readable name for the tool.\n\nDisplayed in the UI and used to identify the tool's purpose.\n"},"description":{"type":"string","maxLength":5120,"description":"Optional detailed description of what the tool does.\n\nUse this to document the tool's purpose, expected inputs/outputs,\nand any special considerations.\n"},"_integrationId":{"type":"string","format":"objectId","description":"Reference to the integration this tool belongs to.\n\nEvery tool must be associated with an integration. The integration\ndetermines the scope and access controls for the tool.\n"},"input":{"$ref":"#/components/schemas/Input"},"output":{"$ref":"#/components/schemas/Output"},"routers":{"type":"array","description":"Optional routers for conditional processing logic.\n\nRouters allow you to direct input data to different processing branches\nbased on filter criteria or script logic. Tools only support\n\"first_matching_branch\" routing strategy.\n\nBranches can chain to other routers or use the special \"outputRouter\"\nterminal value to exit the tool.\n","items":{"$ref":"#/components/schemas/Router"}},"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"draft":{"type":"boolean","description":"When true, this tool is a draft that auto-deletes when its expiry passes\n(`draftExpiresAt` in the response). Set at creation; an update can clear the\nflag but never set it."}}},"Input":{"type":"object","description":"Configuration for the tool's input processing.\n\nDefines the expected input structure, optional transformations to apply\nbefore routing, and mock data for testing.\n","properties":{"name":{"type":"string","maxLength":200,"description":"Display name for the input configuration.\n"},"description":{"type":"string","maxLength":10240,"description":"Description of the expected input data and its purpose.\n"},"schema":{"type":"object","description":"JSON Schema describing the expected input data structure.\n\nUsed for validation, documentation, and AI-assisted tooling.\nMust be a valid JSON Schema document.\n","additionalProperties":true},"transform":{"$ref":"#/components/schemas/Transform"},"debugUntil":{"type":"string","format":"date-time","description":"Enables debug capture for the tool input stage until this timestamp.\nWhile it is in the future, invocations write input-stage\nrequest/response logs, listed at\n`GET /v1/tools/{_id}/tool_input/requests`. At most 1 hour in the\nfuture. A past timestamp (or omitting the field) stops capture.\nArm/disarm with `PATCH /v1/tools/{_id}` path `/input/debugUntil`.\n"},"mockInput":{"type":"object","description":"Mock data for testing the tool's input processing.\n\nProvides sample input to test transformation logic and routing\nwithout requiring live data. Maximum size: 1MB.\n","additionalProperties":true}}},"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"]},"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"}}}},"Output":{"type":"object","description":"Configuration for the tool's output processing.\n\nDefines how the tool's results are mapped, transformed, and enriched\nbefore being returned. Supports field mappings, lookups for data\nenrichment, and custom script hooks for pre/post-mapping processing.\n","properties":{"name":{"type":"string","maxLength":200,"description":"Display name for the output configuration.\n"},"description":{"type":"string","maxLength":10240,"description":"Description of the output data and its purpose.\n"},"schema":{"type":"object","description":"JSON Schema describing the output data structure.\n\nUsed for documentation and validation of the tool's output.\nMust be a valid JSON Schema document.\n","additionalProperties":true},"mappings":{"description":"Field mappings to transform data into the output format.\n\nMaps data from processing results to the output structure.\nUses Celigo's standard mapping format with extract/generate field paths —\na flat array of mapping entries (each entry may recurse via its own\nnested ``mappings`` for object/array structures).\n","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"lookups":{"type":"array","description":"Lookup tables for data enrichment during output processing.\n\nStatic key-value mappings used to translate values (e.g., status codes,\ncategory names) during output generation.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the lookup, used to reference it from mappings.\n"},"map":{"type":"object","description":"Key-value mapping object. Keys are the input values and\nvalues are the corresponding output values.\n","additionalProperties":true},"default":{"type":"string","description":"Default value returned when the input key is not found in the map.\n"},"allowFailures":{"type":"boolean","description":"Whether to continue processing if the lookup fails to find a match\nand no default is provided.\n"}}}},"hooks":{"type":"object","description":"Custom script hooks for pre- and post-mapping processing.\n\nAllows running custom JavaScript functions before and after\noutput mappings are applied.\n","properties":{"preMap":{"type":"object","description":"Script to run before applying output mappings.\n\nCan modify the data before it is mapped to the output structure.\n","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name to execute within the script"}}},"postMap":{"type":"object","description":"Script to run after applying output mappings.\n\nCan modify the final output data after mappings are applied.\n","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name to execute within the script"}}}}},"debugUntil":{"type":"string","format":"date-time","description":"Enables debug capture for the tool output stage until this timestamp.\nWhile it is in the future, invocations write output-stage\nrequest/response logs, listed at\n`GET /v1/tools/{_id}/tool_output/requests`. At most 1 hour in the\nfuture. A past timestamp (or omitting the field) stops capture.\nArm/disarm with `PATCH /v1/tools/{_id}` path `/output/debugUntil`.\n"},"mockInput":{"type":"object","description":"Mock data for testing the tool's output processing.\n\nProvides sample data that would arrive from the routing/processing\nstage, used to test mapping and lookup logic. Maximum size: 1MB.\n","additionalProperties":true}}},"Router":{"type":"object","description":"Configuration for conditional routing within a tool.\n\nRouters evaluate input data and direct it to different processing branches\nbased on criteria. This enables complex business logic and conditional\nprocessing within the tool.\n\nUnlike flows, tools only support \"first_matching_branch\" routing strategy.\nBranches can chain to other routers or use the special \"outputRouter\"\nterminal sink to exit the tool and return results.\n","properties":{"id":{"type":"string","description":"Unique identifier for this router within the tool.\n\nUsed to reference this router from other routers' branch `nextRouterId`.\n"},"name":{"type":"string","maxLength":300,"description":"Human-readable name for the router.\n"},"routeRecordsTo":{"type":"string","enum":["first_matching_branch"],"description":"Routing strategy. Tools only support \"first_matching_branch\",\nwhich routes to the first branch whose criteria match the input.\n"},"routeRecordsUsing":{"type":"string","enum":["input_filters","script"],"description":"Method used to evaluate routing criteria.\n\n- **input_filters**: Use declarative filter expressions on each branch\n- **script**: Use a custom JavaScript function to determine the branch\n"},"script":{"type":"object","description":"Script configuration when routeRecordsUsing is \"script\".\n\nThe function should return the name of the branch to route to.\n","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name that returns the branch name"}}},"branches":{"type":"array","description":"List of branches defining different processing paths.\n\nEach branch has optional filter criteria and a set of processing steps.\nRecords are evaluated against branch criteria in order; the first\nmatching branch is selected.\n","items":{"type":"object","properties":{"name":{"type":"string","maxLength":300,"description":"Name of this branch.\n"},"description":{"type":"string","maxLength":10240,"description":"Description of when and why this branch is selected.\n"},"branchId":{"type":"string","description":"Stable identifier for this branch within the tool, generated by\nthe builder. Used to reference the branch independently of its\nposition in the branches array (e.g., from step requests).\n"},"inputFilter":{"type":"object","description":"Filter criteria to determine if this branch should be selected.\n\nUses Celigo's expression-based filter format.\n","properties":{"version":{"type":"string","enum":["1"],"description":"Filter version"},"rules":{"type":"array","description":"Filter rules in Celigo expression-based filter format.\n\nArray-based DSL where the first element is an operator (e.g., \"equals\", \"and\", \"or\"),\nfollowed by operands which can be nested expressions.\n","items":{}}}},"nextRouterId":{"type":"string","description":"Identifier of the next router to chain to after this branch completes.\n\nUse \"outputRouter\" as a special terminal value to exit the tool\nand return the processing results.\n"},"pageProcessors":{"type":"array","description":"Processing steps to execute in this branch.\n\nEach processor references an export (lookup) or import resource\nfor data retrieval or submission.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["export","import"],"description":"Type of processor.\n\n- **export**: Retrieves data from an external system (lookup)\n- **import**: Sends data to an external system\n"},"_exportId":{"type":"string","format":"objectId","description":"Export resource reference (when type is \"export\")"},"_importId":{"type":"string","format":"objectId","description":"Import resource reference (when type is \"import\")"},"proceedOnFailure":{"type":"boolean","description":"Whether to continue processing subsequent steps if this\nprocessor fails.\n"},"setupInProgress":{"type":"boolean","description":"When true, the processor's configuration is still being\nset up in the UI and the step is not yet runnable.\n"},"responseMapping":{"type":"object","description":"Merges fields from this processor's response back onto the\nin-flight record so later processors and the tool's output\ncan read them. Extracts do NOT read the raw application\nresponse — they evaluate against the platform's canonical\nper-record envelope: for lookups (`type: \"export\"`) that is\n`{\"statusCode\", \"data\": [<result records>], \"errors\"}`, so\npaths must start from `data` (e.g. `data[0].name`); for\nimports it is `{\"id\", \"statusCode\", \"ignored\", \"_json\"}`,\nso use `id` or `_json.<path>`. Bare result-record field\nnames resolve to nothing and merge nothing.\n","properties":{"fields":{"type":"array","description":"Simple field-level mappings","items":{"type":"object","properties":{"extract":{"type":"string","description":"Path within the canonical response envelope to\ncopy the value from (`data[0].x` / `data.0.x`\nfor lookups; `id` or `_json.<path>` for\nimports).\n"},"generate":{"type":"string","description":"Field path on the in-flight record where the\nextracted value is stored (dot notation for\nnesting).\n"}}}},"lists":{"type":"array","description":"List-level mappings for array data","items":{"type":"object","properties":{"generate":{"type":"string","description":"Target list path"},"fields":{"type":"array","description":"Field-level mappings applied to each item in the list.","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path"},"generate":{"type":"string","description":"Target field path"}}}}}}}}},"hooks":{"type":"object","description":"Custom scripts for processing","properties":{"postResponseMap":{"type":"object","description":"Script to run after response mapping","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name to execute"}}}}}}}}}}}}},"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"}}},"Tool":{"type":"object","required":["_id","name","_integrationId","createdAt","lastModified"],"description":"Tool object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ToolBase"},{"$ref":"#/components/schemas/ResourceResponse"},{"type":"object","properties":{"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Origin resource ID when this tool was created by cloning or installing a template."},"draftExpiresAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when a draft tool auto-deletes. Server-computed when `draft` is set at\ncreation."}}}]},"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"]},"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"]}}}}}},"paths":{"/v1/tools":{"post":{"summary":"Create a tool","description":"Creates a new tool within an integration. `name` and `_integrationId`\nare required. Routers use `first_matching_branch` strategy only, and each\nbranch's `nextRouterId` must point to another router's `id` or\n`\"outputRouter\"` to exit the tool.","operationId":"createTool","tags":["Tools"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Request"}}}},"responses":{"201":{"description":"Tool created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tool"}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"422":{"description":"Validation failed. Notably, nesting tools inside tools beyond 5\nlevels is rejected with `tool_nesting_depth_exceeded` (\"Tool cannot\nbe added. It exceeds the maximum nesting depth of 5 levels.\").","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
````

## Get a tool

> Returns the complete configuration of a specific tool.

````json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"tags":[{"name":"Tools","description":"Tools are reusable processing units within integrations that encapsulate input\ntransformation, conditional routing, output mapping, and data enrichment logic behind\nan input/output contract. They can be referenced from flows, APIs, AI agents, MCP\nservers, and other tools to promote modularity and reuse.\n\n## Tool schema\n\n{% openapi-schemas spec=\"tool\" schemas=\"Tool\" 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":{"Tool":{"type":"object","required":["_id","name","_integrationId","createdAt","lastModified"],"description":"Tool object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ToolBase"},{"$ref":"#/components/schemas/ResourceResponse"},{"type":"object","properties":{"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Origin resource ID when this tool was created by cloning or installing a template."},"draftExpiresAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when a draft tool auto-deletes. Server-computed when `draft` is set at\ncreation."}}}]},"ToolBase":{"type":"object","description":"Writable tool fields shared by the request and response schemas.","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Human-readable name for the tool.\n\nDisplayed in the UI and used to identify the tool's purpose.\n"},"description":{"type":"string","maxLength":5120,"description":"Optional detailed description of what the tool does.\n\nUse this to document the tool's purpose, expected inputs/outputs,\nand any special considerations.\n"},"_integrationId":{"type":"string","format":"objectId","description":"Reference to the integration this tool belongs to.\n\nEvery tool must be associated with an integration. The integration\ndetermines the scope and access controls for the tool.\n"},"input":{"$ref":"#/components/schemas/Input"},"output":{"$ref":"#/components/schemas/Output"},"routers":{"type":"array","description":"Optional routers for conditional processing logic.\n\nRouters allow you to direct input data to different processing branches\nbased on filter criteria or script logic. Tools only support\n\"first_matching_branch\" routing strategy.\n\nBranches can chain to other routers or use the special \"outputRouter\"\nterminal value to exit the tool.\n","items":{"$ref":"#/components/schemas/Router"}},"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"draft":{"type":"boolean","description":"When true, this tool is a draft that auto-deletes when its expiry passes\n(`draftExpiresAt` in the response). Set at creation; an update can clear the\nflag but never set it."}}},"Input":{"type":"object","description":"Configuration for the tool's input processing.\n\nDefines the expected input structure, optional transformations to apply\nbefore routing, and mock data for testing.\n","properties":{"name":{"type":"string","maxLength":200,"description":"Display name for the input configuration.\n"},"description":{"type":"string","maxLength":10240,"description":"Description of the expected input data and its purpose.\n"},"schema":{"type":"object","description":"JSON Schema describing the expected input data structure.\n\nUsed for validation, documentation, and AI-assisted tooling.\nMust be a valid JSON Schema document.\n","additionalProperties":true},"transform":{"$ref":"#/components/schemas/Transform"},"debugUntil":{"type":"string","format":"date-time","description":"Enables debug capture for the tool input stage until this timestamp.\nWhile it is in the future, invocations write input-stage\nrequest/response logs, listed at\n`GET /v1/tools/{_id}/tool_input/requests`. At most 1 hour in the\nfuture. A past timestamp (or omitting the field) stops capture.\nArm/disarm with `PATCH /v1/tools/{_id}` path `/input/debugUntil`.\n"},"mockInput":{"type":"object","description":"Mock data for testing the tool's input processing.\n\nProvides sample input to test transformation logic and routing\nwithout requiring live data. Maximum size: 1MB.\n","additionalProperties":true}}},"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"]},"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"}}}},"Output":{"type":"object","description":"Configuration for the tool's output processing.\n\nDefines how the tool's results are mapped, transformed, and enriched\nbefore being returned. Supports field mappings, lookups for data\nenrichment, and custom script hooks for pre/post-mapping processing.\n","properties":{"name":{"type":"string","maxLength":200,"description":"Display name for the output configuration.\n"},"description":{"type":"string","maxLength":10240,"description":"Description of the output data and its purpose.\n"},"schema":{"type":"object","description":"JSON Schema describing the output data structure.\n\nUsed for documentation and validation of the tool's output.\nMust be a valid JSON Schema document.\n","additionalProperties":true},"mappings":{"description":"Field mappings to transform data into the output format.\n\nMaps data from processing results to the output structure.\nUses Celigo's standard mapping format with extract/generate field paths —\na flat array of mapping entries (each entry may recurse via its own\nnested ``mappings`` for object/array structures).\n","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"lookups":{"type":"array","description":"Lookup tables for data enrichment during output processing.\n\nStatic key-value mappings used to translate values (e.g., status codes,\ncategory names) during output generation.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the lookup, used to reference it from mappings.\n"},"map":{"type":"object","description":"Key-value mapping object. Keys are the input values and\nvalues are the corresponding output values.\n","additionalProperties":true},"default":{"type":"string","description":"Default value returned when the input key is not found in the map.\n"},"allowFailures":{"type":"boolean","description":"Whether to continue processing if the lookup fails to find a match\nand no default is provided.\n"}}}},"hooks":{"type":"object","description":"Custom script hooks for pre- and post-mapping processing.\n\nAllows running custom JavaScript functions before and after\noutput mappings are applied.\n","properties":{"preMap":{"type":"object","description":"Script to run before applying output mappings.\n\nCan modify the data before it is mapped to the output structure.\n","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name to execute within the script"}}},"postMap":{"type":"object","description":"Script to run after applying output mappings.\n\nCan modify the final output data after mappings are applied.\n","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name to execute within the script"}}}}},"debugUntil":{"type":"string","format":"date-time","description":"Enables debug capture for the tool output stage until this timestamp.\nWhile it is in the future, invocations write output-stage\nrequest/response logs, listed at\n`GET /v1/tools/{_id}/tool_output/requests`. At most 1 hour in the\nfuture. A past timestamp (or omitting the field) stops capture.\nArm/disarm with `PATCH /v1/tools/{_id}` path `/output/debugUntil`.\n"},"mockInput":{"type":"object","description":"Mock data for testing the tool's output processing.\n\nProvides sample data that would arrive from the routing/processing\nstage, used to test mapping and lookup logic. Maximum size: 1MB.\n","additionalProperties":true}}},"Router":{"type":"object","description":"Configuration for conditional routing within a tool.\n\nRouters evaluate input data and direct it to different processing branches\nbased on criteria. This enables complex business logic and conditional\nprocessing within the tool.\n\nUnlike flows, tools only support \"first_matching_branch\" routing strategy.\nBranches can chain to other routers or use the special \"outputRouter\"\nterminal sink to exit the tool and return results.\n","properties":{"id":{"type":"string","description":"Unique identifier for this router within the tool.\n\nUsed to reference this router from other routers' branch `nextRouterId`.\n"},"name":{"type":"string","maxLength":300,"description":"Human-readable name for the router.\n"},"routeRecordsTo":{"type":"string","enum":["first_matching_branch"],"description":"Routing strategy. Tools only support \"first_matching_branch\",\nwhich routes to the first branch whose criteria match the input.\n"},"routeRecordsUsing":{"type":"string","enum":["input_filters","script"],"description":"Method used to evaluate routing criteria.\n\n- **input_filters**: Use declarative filter expressions on each branch\n- **script**: Use a custom JavaScript function to determine the branch\n"},"script":{"type":"object","description":"Script configuration when routeRecordsUsing is \"script\".\n\nThe function should return the name of the branch to route to.\n","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name that returns the branch name"}}},"branches":{"type":"array","description":"List of branches defining different processing paths.\n\nEach branch has optional filter criteria and a set of processing steps.\nRecords are evaluated against branch criteria in order; the first\nmatching branch is selected.\n","items":{"type":"object","properties":{"name":{"type":"string","maxLength":300,"description":"Name of this branch.\n"},"description":{"type":"string","maxLength":10240,"description":"Description of when and why this branch is selected.\n"},"branchId":{"type":"string","description":"Stable identifier for this branch within the tool, generated by\nthe builder. Used to reference the branch independently of its\nposition in the branches array (e.g., from step requests).\n"},"inputFilter":{"type":"object","description":"Filter criteria to determine if this branch should be selected.\n\nUses Celigo's expression-based filter format.\n","properties":{"version":{"type":"string","enum":["1"],"description":"Filter version"},"rules":{"type":"array","description":"Filter rules in Celigo expression-based filter format.\n\nArray-based DSL where the first element is an operator (e.g., \"equals\", \"and\", \"or\"),\nfollowed by operands which can be nested expressions.\n","items":{}}}},"nextRouterId":{"type":"string","description":"Identifier of the next router to chain to after this branch completes.\n\nUse \"outputRouter\" as a special terminal value to exit the tool\nand return the processing results.\n"},"pageProcessors":{"type":"array","description":"Processing steps to execute in this branch.\n\nEach processor references an export (lookup) or import resource\nfor data retrieval or submission.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["export","import"],"description":"Type of processor.\n\n- **export**: Retrieves data from an external system (lookup)\n- **import**: Sends data to an external system\n"},"_exportId":{"type":"string","format":"objectId","description":"Export resource reference (when type is \"export\")"},"_importId":{"type":"string","format":"objectId","description":"Import resource reference (when type is \"import\")"},"proceedOnFailure":{"type":"boolean","description":"Whether to continue processing subsequent steps if this\nprocessor fails.\n"},"setupInProgress":{"type":"boolean","description":"When true, the processor's configuration is still being\nset up in the UI and the step is not yet runnable.\n"},"responseMapping":{"type":"object","description":"Merges fields from this processor's response back onto the\nin-flight record so later processors and the tool's output\ncan read them. Extracts do NOT read the raw application\nresponse — they evaluate against the platform's canonical\nper-record envelope: for lookups (`type: \"export\"`) that is\n`{\"statusCode\", \"data\": [<result records>], \"errors\"}`, so\npaths must start from `data` (e.g. `data[0].name`); for\nimports it is `{\"id\", \"statusCode\", \"ignored\", \"_json\"}`,\nso use `id` or `_json.<path>`. Bare result-record field\nnames resolve to nothing and merge nothing.\n","properties":{"fields":{"type":"array","description":"Simple field-level mappings","items":{"type":"object","properties":{"extract":{"type":"string","description":"Path within the canonical response envelope to\ncopy the value from (`data[0].x` / `data.0.x`\nfor lookups; `id` or `_json.<path>` for\nimports).\n"},"generate":{"type":"string","description":"Field path on the in-flight record where the\nextracted value is stored (dot notation for\nnesting).\n"}}}},"lists":{"type":"array","description":"List-level mappings for array data","items":{"type":"object","properties":{"generate":{"type":"string","description":"Target list path"},"fields":{"type":"array","description":"Field-level mappings applied to each item in the list.","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path"},"generate":{"type":"string","description":"Target field path"}}}}}}}}},"hooks":{"type":"object","description":"Custom scripts for processing","properties":{"postResponseMap":{"type":"object","description":"Script to run after response mapping","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name to execute"}}}}}}}}}}}}},"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"}}},"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"]},"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/tools/{_id}":{"get":{"summary":"Get a tool","description":"Returns the complete configuration of a specific tool.","operationId":"getToolById","tags":["Tools"],"parameters":[{"name":"_id","in":"path","description":"The unique identifier of the tool","required":true,"schema":{"type":"string","format":"objectId"}}],"responses":{"200":{"description":"Tool retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tool"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
````

## Update a tool

> Replaces the tool configuration. This is a full replacement — GET the\
> tool first, modify the fields you need, then PUT the full object back.\
> Omitting a field removes it. \`name\` and \`\_integrationId\` are required\
> on every PUT.

````json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"tags":[{"name":"Tools","description":"Tools are reusable processing units within integrations that encapsulate input\ntransformation, conditional routing, output mapping, and data enrichment logic behind\nan input/output contract. They can be referenced from flows, APIs, AI agents, MCP\nservers, and other tools to promote modularity and reuse.\n\n## Tool schema\n\n{% openapi-schemas spec=\"tool\" schemas=\"Tool\" 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":"Request schema for creating or updating a tool. Tools are reusable processing\nunits that encapsulate input transformation, conditional routing, and output\nmapping logic within an integration.","required":["name","_integrationId"],"allOf":[{"$ref":"#/components/schemas/ToolBase"}]},"ToolBase":{"type":"object","description":"Writable tool fields shared by the request and response schemas.","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Human-readable name for the tool.\n\nDisplayed in the UI and used to identify the tool's purpose.\n"},"description":{"type":"string","maxLength":5120,"description":"Optional detailed description of what the tool does.\n\nUse this to document the tool's purpose, expected inputs/outputs,\nand any special considerations.\n"},"_integrationId":{"type":"string","format":"objectId","description":"Reference to the integration this tool belongs to.\n\nEvery tool must be associated with an integration. The integration\ndetermines the scope and access controls for the tool.\n"},"input":{"$ref":"#/components/schemas/Input"},"output":{"$ref":"#/components/schemas/Output"},"routers":{"type":"array","description":"Optional routers for conditional processing logic.\n\nRouters allow you to direct input data to different processing branches\nbased on filter criteria or script logic. Tools only support\n\"first_matching_branch\" routing strategy.\n\nBranches can chain to other routers or use the special \"outputRouter\"\nterminal value to exit the tool.\n","items":{"$ref":"#/components/schemas/Router"}},"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"draft":{"type":"boolean","description":"When true, this tool is a draft that auto-deletes when its expiry passes\n(`draftExpiresAt` in the response). Set at creation; an update can clear the\nflag but never set it."}}},"Input":{"type":"object","description":"Configuration for the tool's input processing.\n\nDefines the expected input structure, optional transformations to apply\nbefore routing, and mock data for testing.\n","properties":{"name":{"type":"string","maxLength":200,"description":"Display name for the input configuration.\n"},"description":{"type":"string","maxLength":10240,"description":"Description of the expected input data and its purpose.\n"},"schema":{"type":"object","description":"JSON Schema describing the expected input data structure.\n\nUsed for validation, documentation, and AI-assisted tooling.\nMust be a valid JSON Schema document.\n","additionalProperties":true},"transform":{"$ref":"#/components/schemas/Transform"},"debugUntil":{"type":"string","format":"date-time","description":"Enables debug capture for the tool input stage until this timestamp.\nWhile it is in the future, invocations write input-stage\nrequest/response logs, listed at\n`GET /v1/tools/{_id}/tool_input/requests`. At most 1 hour in the\nfuture. A past timestamp (or omitting the field) stops capture.\nArm/disarm with `PATCH /v1/tools/{_id}` path `/input/debugUntil`.\n"},"mockInput":{"type":"object","description":"Mock data for testing the tool's input processing.\n\nProvides sample input to test transformation logic and routing\nwithout requiring live data. Maximum size: 1MB.\n","additionalProperties":true}}},"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"]},"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"}}}},"Output":{"type":"object","description":"Configuration for the tool's output processing.\n\nDefines how the tool's results are mapped, transformed, and enriched\nbefore being returned. Supports field mappings, lookups for data\nenrichment, and custom script hooks for pre/post-mapping processing.\n","properties":{"name":{"type":"string","maxLength":200,"description":"Display name for the output configuration.\n"},"description":{"type":"string","maxLength":10240,"description":"Description of the output data and its purpose.\n"},"schema":{"type":"object","description":"JSON Schema describing the output data structure.\n\nUsed for documentation and validation of the tool's output.\nMust be a valid JSON Schema document.\n","additionalProperties":true},"mappings":{"description":"Field mappings to transform data into the output format.\n\nMaps data from processing results to the output structure.\nUses Celigo's standard mapping format with extract/generate field paths —\na flat array of mapping entries (each entry may recurse via its own\nnested ``mappings`` for object/array structures).\n","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"lookups":{"type":"array","description":"Lookup tables for data enrichment during output processing.\n\nStatic key-value mappings used to translate values (e.g., status codes,\ncategory names) during output generation.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the lookup, used to reference it from mappings.\n"},"map":{"type":"object","description":"Key-value mapping object. Keys are the input values and\nvalues are the corresponding output values.\n","additionalProperties":true},"default":{"type":"string","description":"Default value returned when the input key is not found in the map.\n"},"allowFailures":{"type":"boolean","description":"Whether to continue processing if the lookup fails to find a match\nand no default is provided.\n"}}}},"hooks":{"type":"object","description":"Custom script hooks for pre- and post-mapping processing.\n\nAllows running custom JavaScript functions before and after\noutput mappings are applied.\n","properties":{"preMap":{"type":"object","description":"Script to run before applying output mappings.\n\nCan modify the data before it is mapped to the output structure.\n","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name to execute within the script"}}},"postMap":{"type":"object","description":"Script to run after applying output mappings.\n\nCan modify the final output data after mappings are applied.\n","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name to execute within the script"}}}}},"debugUntil":{"type":"string","format":"date-time","description":"Enables debug capture for the tool output stage until this timestamp.\nWhile it is in the future, invocations write output-stage\nrequest/response logs, listed at\n`GET /v1/tools/{_id}/tool_output/requests`. At most 1 hour in the\nfuture. A past timestamp (or omitting the field) stops capture.\nArm/disarm with `PATCH /v1/tools/{_id}` path `/output/debugUntil`.\n"},"mockInput":{"type":"object","description":"Mock data for testing the tool's output processing.\n\nProvides sample data that would arrive from the routing/processing\nstage, used to test mapping and lookup logic. Maximum size: 1MB.\n","additionalProperties":true}}},"Router":{"type":"object","description":"Configuration for conditional routing within a tool.\n\nRouters evaluate input data and direct it to different processing branches\nbased on criteria. This enables complex business logic and conditional\nprocessing within the tool.\n\nUnlike flows, tools only support \"first_matching_branch\" routing strategy.\nBranches can chain to other routers or use the special \"outputRouter\"\nterminal sink to exit the tool and return results.\n","properties":{"id":{"type":"string","description":"Unique identifier for this router within the tool.\n\nUsed to reference this router from other routers' branch `nextRouterId`.\n"},"name":{"type":"string","maxLength":300,"description":"Human-readable name for the router.\n"},"routeRecordsTo":{"type":"string","enum":["first_matching_branch"],"description":"Routing strategy. Tools only support \"first_matching_branch\",\nwhich routes to the first branch whose criteria match the input.\n"},"routeRecordsUsing":{"type":"string","enum":["input_filters","script"],"description":"Method used to evaluate routing criteria.\n\n- **input_filters**: Use declarative filter expressions on each branch\n- **script**: Use a custom JavaScript function to determine the branch\n"},"script":{"type":"object","description":"Script configuration when routeRecordsUsing is \"script\".\n\nThe function should return the name of the branch to route to.\n","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name that returns the branch name"}}},"branches":{"type":"array","description":"List of branches defining different processing paths.\n\nEach branch has optional filter criteria and a set of processing steps.\nRecords are evaluated against branch criteria in order; the first\nmatching branch is selected.\n","items":{"type":"object","properties":{"name":{"type":"string","maxLength":300,"description":"Name of this branch.\n"},"description":{"type":"string","maxLength":10240,"description":"Description of when and why this branch is selected.\n"},"branchId":{"type":"string","description":"Stable identifier for this branch within the tool, generated by\nthe builder. Used to reference the branch independently of its\nposition in the branches array (e.g., from step requests).\n"},"inputFilter":{"type":"object","description":"Filter criteria to determine if this branch should be selected.\n\nUses Celigo's expression-based filter format.\n","properties":{"version":{"type":"string","enum":["1"],"description":"Filter version"},"rules":{"type":"array","description":"Filter rules in Celigo expression-based filter format.\n\nArray-based DSL where the first element is an operator (e.g., \"equals\", \"and\", \"or\"),\nfollowed by operands which can be nested expressions.\n","items":{}}}},"nextRouterId":{"type":"string","description":"Identifier of the next router to chain to after this branch completes.\n\nUse \"outputRouter\" as a special terminal value to exit the tool\nand return the processing results.\n"},"pageProcessors":{"type":"array","description":"Processing steps to execute in this branch.\n\nEach processor references an export (lookup) or import resource\nfor data retrieval or submission.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["export","import"],"description":"Type of processor.\n\n- **export**: Retrieves data from an external system (lookup)\n- **import**: Sends data to an external system\n"},"_exportId":{"type":"string","format":"objectId","description":"Export resource reference (when type is \"export\")"},"_importId":{"type":"string","format":"objectId","description":"Import resource reference (when type is \"import\")"},"proceedOnFailure":{"type":"boolean","description":"Whether to continue processing subsequent steps if this\nprocessor fails.\n"},"setupInProgress":{"type":"boolean","description":"When true, the processor's configuration is still being\nset up in the UI and the step is not yet runnable.\n"},"responseMapping":{"type":"object","description":"Merges fields from this processor's response back onto the\nin-flight record so later processors and the tool's output\ncan read them. Extracts do NOT read the raw application\nresponse — they evaluate against the platform's canonical\nper-record envelope: for lookups (`type: \"export\"`) that is\n`{\"statusCode\", \"data\": [<result records>], \"errors\"}`, so\npaths must start from `data` (e.g. `data[0].name`); for\nimports it is `{\"id\", \"statusCode\", \"ignored\", \"_json\"}`,\nso use `id` or `_json.<path>`. Bare result-record field\nnames resolve to nothing and merge nothing.\n","properties":{"fields":{"type":"array","description":"Simple field-level mappings","items":{"type":"object","properties":{"extract":{"type":"string","description":"Path within the canonical response envelope to\ncopy the value from (`data[0].x` / `data.0.x`\nfor lookups; `id` or `_json.<path>` for\nimports).\n"},"generate":{"type":"string","description":"Field path on the in-flight record where the\nextracted value is stored (dot notation for\nnesting).\n"}}}},"lists":{"type":"array","description":"List-level mappings for array data","items":{"type":"object","properties":{"generate":{"type":"string","description":"Target list path"},"fields":{"type":"array","description":"Field-level mappings applied to each item in the list.","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path"},"generate":{"type":"string","description":"Target field path"}}}}}}}}},"hooks":{"type":"object","description":"Custom scripts for processing","properties":{"postResponseMap":{"type":"object","description":"Script to run after response mapping","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name to execute"}}}}}}}}}}}}},"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"}}},"Tool":{"type":"object","required":["_id","name","_integrationId","createdAt","lastModified"],"description":"Tool object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ToolBase"},{"$ref":"#/components/schemas/ResourceResponse"},{"type":"object","properties":{"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Origin resource ID when this tool was created by cloning or installing a template."},"draftExpiresAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when a draft tool auto-deletes. Server-computed when `draft` is set at\ncreation."}}}]},"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"]},"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/tools/{_id}":{"put":{"summary":"Update a tool","description":"Replaces the tool configuration. This is a full replacement — GET the\ntool first, modify the fields you need, then PUT the full object back.\nOmitting a field removes it. `name` and `_integrationId` are required\non every PUT.","operationId":"updateTool","tags":["Tools"],"parameters":[{"name":"_id","in":"path","description":"The unique identifier of the tool","required":true,"schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Request"}}}},"responses":{"200":{"description":"Tool updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tool"}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"},"422":{"description":"Validation failed. Notably, nesting tools inside tools beyond 5\nlevels is rejected with `tool_nesting_depth_exceeded` (\"Tool cannot\nbe added. It exceeds the maximum nesting depth of 5 levels.\").","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
````

## Delete a tool

> Deletes a tool. Soft-deleted and retained in the recycle bin for 30 days.\
> Fails with 422 if other resources (MCP servers, access tokens) still\
> reference this tool — call \`GET /v1/tools/{\_id}/dependencies\` first to\
> check.

```json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"tags":[{"name":"Tools","description":"Tools are reusable processing units within integrations that encapsulate input\ntransformation, conditional routing, output mapping, and data enrichment logic behind\nan input/output contract. They can be referenced from flows, APIs, AI agents, MCP\nservers, and other tools to promote modularity and reuse.\n\n## Tool schema\n\n{% openapi-schemas spec=\"tool\" schemas=\"Tool\" 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/tools/{_id}":{"delete":{"summary":"Delete a tool","description":"Deletes a tool. Soft-deleted and retained in the recycle bin for 30 days.\nFails with 422 if other resources (MCP servers, access tokens) still\nreference this tool — call `GET /v1/tools/{_id}/dependencies` first to\ncheck.","operationId":"deleteTool","tags":["Tools"],"parameters":[{"name":"_id","in":"path","description":"The unique identifier of the tool","required":true,"schema":{"type":"string","format":"objectId"}}],"responses":{"204":{"description":"Tool deleted successfully"},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"},"422":{"$ref":"#/components/responses/422-dependency-conflict"}}}}}}
```

## Patch a tool

> Partially updates a tool using a JSON Patch document (RFC 6902).\
> The \`add\`, \`remove\`, \`replace\`, and \`move\` operations are supported\
> (\`copy\` and \`test\` are rejected), and only on the following whitelisted\
> paths:\
> \
> \| Path | Description |\
> \|------|-------------|\
> \| \`/name\` | Tool display name |\
> \| \`/description\` | Tool description |\
> \| \`/aiDescription\` | AI-generated description object |\
> \| \`/input/debugUntil\` | Tool Input debug capture expiry (ISO-8601, at most 1 hour in the future; 422 beyond the cap) |\
> \| \`/output/debugUntil\` | Tool Output debug capture expiry (ISO-8601, at most 1 hour in the future; 422 beyond the cap) |\
> \
> All other paths are rejected with 422 (\`not a whitelisted property\`).\
> \
> PATCH is the intended way to update these fields without accidentally\
> resetting other tool configuration via \`PUT\`. In particular, it is how\
> you arm and disarm step debug capture: \`replace\` \`/input/debugUntil\` or\
> \`/output/debugUntil\` with a future ISO timestamp to start capturing, or\
> a past timestamp to stop. While armed, captured logs are readable at\
> \`GET /v1/tools/{\_id}/tool\_input/requests\` and\
> \`GET /v1/tools/{\_id}/tool\_output/requests\`.

```json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"tags":[{"name":"Tools","description":"Tools are reusable processing units within integrations that encapsulate input\ntransformation, conditional routing, output mapping, and data enrichment logic behind\nan input/output contract. They can be referenced from flows, APIs, AI agents, MCP\nservers, and other tools to promote modularity and reuse.\n\n## Tool schema\n\n{% openapi-schemas spec=\"tool\" schemas=\"Tool\" 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/tools/{_id}":{"patch":{"summary":"Patch a tool","description":"Partially updates a tool using a JSON Patch document (RFC 6902).\nThe `add`, `remove`, `replace`, and `move` operations are supported\n(`copy` and `test` are rejected), and only on the following whitelisted\npaths:\n\n| Path | Description |\n|------|-------------|\n| `/name` | Tool display name |\n| `/description` | Tool description |\n| `/aiDescription` | AI-generated description object |\n| `/input/debugUntil` | Tool Input debug capture expiry (ISO-8601, at most 1 hour in the future; 422 beyond the cap) |\n| `/output/debugUntil` | Tool Output debug capture expiry (ISO-8601, at most 1 hour in the future; 422 beyond the cap) |\n\nAll other paths are rejected with 422 (`not a whitelisted property`).\n\nPATCH is the intended way to update these fields without accidentally\nresetting other tool configuration via `PUT`. In particular, it is how\nyou arm and disarm step debug capture: `replace` `/input/debugUntil` or\n`/output/debugUntil` with a future ISO timestamp to start capturing, or\na past timestamp to stop. While armed, captured logs are readable at\n`GET /v1/tools/{_id}/tool_input/requests` and\n`GET /v1/tools/{_id}/tool_output/requests`.","operationId":"patchTool","tags":["Tools"],"parameters":[{"name":"_id","in":"path","description":"The unique identifier of the tool","required":true,"schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonPatchRequest"}}}},"responses":{"204":{"description":"Tool patched successfully."},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
```

## List connections a tool depends on

> Returns the full Connection resources the tool references — both directly\
> (via \`\_connectionId\` fields on its steps) and transitively through\
> descendant resources (inner tools, lookups, imports, exports).\
> \
> Useful for discovering what systems a tool talks to before cloning, moving,\
> or evaluating the blast radius of a connection change. For the full\
> dependency tree (imports, exports, nested tools), use\
> \`GET /v1/tools/{\_id}/descendants\` instead.

```json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"tags":[{"name":"Tools","description":"Tools are reusable processing units within integrations that encapsulate input\ntransformation, conditional routing, output mapping, and data enrichment logic behind\nan input/output contract. They can be referenced from flows, APIs, AI agents, MCP\nservers, and other tools to promote modularity and reuse.\n\n## Tool schema\n\n{% openapi-schemas spec=\"tool\" schemas=\"Tool\" 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":{"Connection":{"type":"object","required":["_id","name","type","createdAt","lastModified"],"description":"Connection object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ResponseBase"},{"type":"object","properties":{"netsuite":{"description":"Present when `type` is `netsuite`.","allOf":[{"$ref":"#/components/schemas/NetSuite"},{"type":"object","properties":{"suiteAppInstalled":{"type":"boolean","readOnly":true,"description":"When true, the Celigo integrator.io SuiteApp (SuiteScript 2.x) was detected in\nthe connected NetSuite account the last time the connection authenticated. It\nsays nothing about the legacy Celigo bundle; for a live check of both, use\n`GET /v1/connections/{_id}/distributedApps`."}}}]},"salesforce":{"description":"Present when `type` is `salesforce`.","allOf":[{"$ref":"#/components/schemas/Salesforce"},{"type":"object","properties":{"info":{"type":"object","readOnly":true,"description":"Salesforce org and user metadata populated by the server after a successful\nauthentication (the OpenID Connect userinfo of the authorizing user). Read-only.","properties":{"user_id":{"type":"string","readOnly":true,"description":"Salesforce user ID of the authorizing user."},"organization_id":{"type":"string","readOnly":true,"description":"Salesforce organization (org) ID this connection authenticates to."},"email":{"type":"string","format":"email","readOnly":true,"description":"Email address of the authorizing Salesforce user."},"preferred_username":{"type":"string","readOnly":true,"description":"Salesforce login username of the authorizing user."},"name":{"type":"string","readOnly":true,"description":"Full display name of the authorizing user."},"is_salesforce_integration_user":{"type":"boolean","readOnly":true,"description":"When true, the authorizing user is a Salesforce Integration User license."}}}}}]},"ftp":{"description":"Present when `type` is `ftp`.","allOf":[{"$ref":"#/components/schemas/FTP"}]},"s3":{"description":"Present when `type` is `s3`.","allOf":[{"$ref":"#/components/schemas/S3"}]},"http":{"description":"Present when `type` is `http`.","allOf":[{"$ref":"#/components/schemas/HTTP"}]},"rdbms":{"description":"Present when `type` is `rdbms`.","allOf":[{"$ref":"#/components/schemas/RDBMS"}]},"mongodb":{"description":"Present when `type` is `mongodb`.","allOf":[{"$ref":"#/components/schemas/MongoDB"}]},"as2":{"description":"Present when `type` is `as2`.","allOf":[{"$ref":"#/components/schemas/AS2"}]},"filesystem":{"description":"Present when `type` is `filesystem`.","allOf":[{"$ref":"#/components/schemas/Filesystem"}]},"mcp":{"description":"Present when `type` is `mcp`.","allOf":[{"$ref":"#/components/schemas/MCP"}]},"dynamodb":{"description":"Present when `type` is `dynamodb`.","allOf":[{"$ref":"#/components/schemas/DynamoDB"}]},"jdbc":{"description":"Present when `type` is `jdbc`.","allOf":[{"$ref":"#/components/schemas/JDBC"}]},"van":{"description":"Present when `type` is `van`.","allOf":[{"$ref":"#/components/schemas/VAN"}]},"wrapper":{"description":"Present when `type` is `wrapper`.","allOf":[{"$ref":"#/components/schemas/Wrapper"}]}}}],"if":{"properties":{"type":{"const":"netsuite"}},"required":["type"]},"then":{"required":["netsuite"]},"else":{"if":{"properties":{"type":{"const":"salesforce"}},"required":["type"]},"then":{"required":["salesforce"]},"else":{"if":{"properties":{"type":{"const":"ftp"}},"required":["type"]},"then":{"required":["ftp"]},"else":{"if":{"properties":{"type":{"const":"s3"}},"required":["type"]},"then":{"required":["s3"]},"else":{"if":{"properties":{"type":{"const":"http"}},"required":["type"]},"then":{"required":["http"]},"else":{"if":{"properties":{"type":{"const":"rdbms"}},"required":["type"]},"then":{"required":["rdbms"]},"else":{"if":{"properties":{"type":{"const":"mongodb"}},"required":["type"]},"then":{"required":["mongodb"]},"else":{"if":{"properties":{"type":{"const":"as2"}},"required":["type"]},"then":{"required":["as2"]},"else":{"if":{"properties":{"type":{"const":"filesystem"}},"required":["type"]},"then":{"required":["filesystem"]},"else":{"if":{"properties":{"type":{"const":"mcp"}},"required":["type"]},"then":{"required":["mcp"]},"else":{"if":{"properties":{"type":{"const":"dynamodb"}},"required":["type"]},"then":{"required":["dynamodb"]},"else":{"if":{"properties":{"type":{"const":"jdbc"}},"required":["type"]},"then":{"required":["jdbc"]},"else":{"if":{"properties":{"type":{"const":"van"}},"required":["type"]},"then":{"required":["van"]},"else":{"if":{"properties":{"type":{"const":"wrapper"}},"required":["type"]},"then":{"required":["wrapper"]},"else":{"if":{"properties":{"type":{"const":"rest"}},"required":["type"]},"then":{"required":["rest"]}}}}}}}}}}}}}}}},"ResponseBase":{"type":"object","description":"Shared properties present on every connection response regardless of type.","allOf":[{"$ref":"#/components/schemas/ResourceResponse"},{"$ref":"#/components/schemas/IAResourceResponse"},{"type":"object","properties":{"name":{"type":"string","description":"Display name for the connection.","maxLength":100},"type":{"type":"string","description":"The type of connection determining which authentication and connectivity options are available","enum":["netsuite","salesforce","ftp","s3","wrapper","http","rdbms","mongodb","as2","filesystem","mcp","dynamodb","jdbc","van","rest"]},"externalId":{"type":"string","description":"External identifier for the connection, often used for integration with third-party systems"},"assistant":{"type":"string","description":"Application name in lowercase for HTTP connections to systems with integrator.io adaptors.\nUsed to identify the target application being connected to.\nExamples - Shopify: \"shopify\", eBay: \"ebay\".\nOnly applicable for HTTP connection types.\n"},"_agentId":{"type":"string","format":"objectId","description":"Reference to a Celigo on-premise Agent. Required for connection types that need\nlocal network or filesystem access (JDBC, filesystem, Oracle RDBMS, and on-premise MongoDB).\nThe agent establishes a secure tunnel between the on-premise environment and integrator.io.\n"},"_borrowConcurrencyFromConnectionId":{"type":"string","format":"objectId","description":"Reference to another connection to share concurrency limits with.\nWhen set, this connection's concurrency is counted against the referenced\nconnection's limit instead of maintaining its own.\n"},"debugDate":{"type":"string","format":"date-time","description":"Date until which debug logging is enabled for this connection"},"settingsForm":{"type":"object","description":"Dynamic form configuration for connection-specific settings"},"settings":{"type":"object","description":"Connection-specific settings and configurations"},"pgp":{"description":"Present for file-based connections (ftp, s3, filesystem, …) that encrypt or decrypt files with PGP.","allOf":[{"$ref":"#/components/schemas/PGP"}]},"ssl":{"$ref":"#/components/schemas/SSL"},"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"offline":{"type":"boolean","description":"When true, the connection has been taken offline and is skipped during flow execution.","readOnly":true},"_sourceId":{"type":"string","format":"objectId","description":"Source connection this was cloned from.","readOnly":true},"_userId":{"type":"string","format":"objectId","description":"User who owns this connection.","readOnly":true},"debugUntil":{"type":"string","format":"date-time","description":"Debug logging is active until this timestamp. Absent or in the past means debug is off.","readOnly":true},"encrypted":{"type":"string","description":"Masked placeholder for encrypted credential fields. Always returns `\"******\"`.","readOnly":true},"isHTTP":{"type":"boolean","description":"When true, the connection uses the HTTP adaptor internally, even when `type` is `wrapper`.","readOnly":true},"autoRecoverRateLimitErrors":{"type":"boolean","default":true,"description":"When true, rate-limit errors from the target system trigger automatic recovery: concurrency\ndrops to 1, the rate-limited requests are retried with doubling waits (1 to 1024 minutes,\nafter which the flag switches itself off), and concurrency climbs back toward\n`targetConcurrencyLevel`. While true, `targetConcurrencyLevel` is the throughput lever: a\n`concurrencyLevel` sent in a PUT is ignored, and one sent in a PATCH is stored but the\nrecovery machinery moves it back toward the target."},"enableMicroBatchForOneToMany":{"type":"boolean","default":true,"description":"When true, enables micro-batching for one-to-many data flows through this connection."},"enableCsvObjectParsing":{"type":"boolean","default":true,"description":"When true, enables CSV-to-object parsing for data received through this connection."},"microServices":{"type":"object","description":"Per-adaptor feature flags controlling which microservice workers handle this connection's traffic.","readOnly":true,"properties":{"disableHttp":{"type":"boolean","description":"When true, the HTTP microservice worker is disabled for this connection."},"disableNetSuiteDistributed":{"type":"boolean","description":"When true, the NetSuite distributed-processing worker is disabled for this connection."},"disableNetSuiteProxy":{"type":"boolean","description":"When true, the NetSuite proxy worker is disabled for this connection."},"disableNetSuiteWebservices":{"type":"boolean","description":"When true, the NetSuite SuiteTalk Web Services worker is disabled for this connection."},"disableRdbms":{"type":"boolean","description":"When true, the RDBMS worker is disabled for this connection."},"disableAs2":{"type":"boolean","description":"When true, the AS2 worker is disabled for this connection."},"disableAs2File":{"type":"boolean","description":"When true, the AS2 file-processing worker is disabled for this connection."},"disableDataLoaderFile":{"type":"boolean","description":"When true, the data-loader file worker is disabled for this connection."},"disableFtp":{"type":"boolean","description":"When true, the FTP worker is disabled for this connection."},"disableS3":{"type":"boolean","description":"When true, the S3 worker is disabled for this connection."},"disableSalesforce":{"type":"boolean","description":"When true, the Salesforce worker is disabled for this connection."},"disableFile":{"type":"boolean","description":"When true, the generic file worker is disabled for this connection."},"disableNsFile":{"type":"boolean","description":"When true, the NetSuite file worker is disabled for this connection."},"workerGroup":{"type":"string","description":"Name of the worker group that processes this connection's traffic."}}},"queues":{"type":"array","description":"Message queue sizes for this connection. Always present on GET-by-ID; on the list endpoint only when `fetchQueueSize=true`.","readOnly":true,"items":{"type":"object","properties":{"name":{"type":"string","description":"Queue identifier (typically the connection ID)."},"size":{"type":"integer","description":"Number of messages currently in the queue."}}}}}}]},"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"}}},"PGP":{"type":"object","description":"PGP encryption settings for file-based connections (ftp, s3, and similar). When set, files are\nPGP-encrypted before upload and/or decrypted after download. Supply at least one of `publicKey`\n(to encrypt outbound files) or `privateKey` (to decrypt inbound files); set `passphrase` whenever\n`privateKey` is provided.","properties":{"publicKey":{"type":"string","description":"ASCII-armored PGP public key used to encrypt outbound files before upload.\nSet this when the connection sends encrypted files; omit it for decrypt-only connections."},"privateKey":{"type":"string","writeOnly":true,"description":"ASCII-armored PGP private key used to decrypt inbound files after download (encrypted at rest;\nreturned masked as `\"******\"`). Set this when the connection receives encrypted files."},"passphrase":{"type":"string","writeOnly":true,"description":"Passphrase that unlocks `privateKey` (encrypted at rest; returned masked as `\"******\"`). Set this whenever `privateKey` is provided."},"compressionAlgorithm":{"type":"string","enum":["zip","zlib"],"description":"Compression applied to the message payload before PGP encryption. Match the algorithm the recipient expects; leave unset to use the server default."},"asciiArmored":{"type":"boolean","default":true,"description":"When true, produces ASCII-armored (text) PGP output. Set to false only when the recipient requires binary PGP output."}},"if":{"required":["privateKey"]},"then":{"required":["passphrase"]}},"SSL":{"type":"object","description":"SSL/TLS certificate configuration for database connections that use client certificate\n(mTLS) authentication or connect to servers with private CA-signed certificates. Provide\ncert and key together for mTLS, ca for a private CA, and passphrase only when the private\nkey is encrypted; cert/key and ca can be combined.","properties":{"ca":{"type":"string","description":"Certificate Authority certificate in PEM format. Set when the database server uses a\ncertificate signed by a private CA not in the system's default trust store.","writeOnly":true},"key":{"type":"string","description":"Client private key in PEM format, paired with cert for mTLS authentication.\nCannot be provided without cert.","writeOnly":true},"passphrase":{"type":"string","description":"Passphrase that decrypts the private key in the key field, when that key is password-protected.","writeOnly":true},"cert":{"type":"string","description":"Client certificate in PEM format, paired with key for mTLS authentication.\nCannot be provided without key.","writeOnly":true}}},"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"}}},"NetSuite":{"type":"object","description":"Configuration for NetSuite ERP connections. Used when the connection type is \"netsuite\".\nThe authType field selects the authentication method; token-based authentication (TBA) is\nrecommended for production.","required":["authType"],"properties":{"authType":{"type":"string","enum":["token","token-auto"],"description":"Authentication method for the NetSuite connection. token-auto delegates the token\nlifecycle to an iClient; token uses a manually-supplied tokenId/tokenSecret pair."},"account":{"type":"string","description":"NetSuite account ID (automatically uppercased), required for token and token-auto authentication.\nFound in NetSuite under Setup > Company > Company Information. Non-production accounts\ncarry an environment suffix (e.g. `123456_SB1`); production and beta use the bare id."},"environment":{"type":"string","enum":["production","sandbox","sandbox2.0","beta"],"description":"NetSuite environment to connect to. Defaults to production when not specified."},"tokenId":{"type":"string","description":"NetSuite TBA token ID (encrypted at rest). Required when authType is \"token\".\n\nGenerated in NetSuite under Setup > Users/Roles > Access Tokens.\nMust be paired with the corresponding tokenSecret.\n","writeOnly":true},"tokenSecret":{"type":"string","description":"NetSuite TBA token secret (encrypted at rest). Required when authType is \"token\".\n\nGenerated alongside the tokenId in NetSuite. Treat as a sensitive credential.\n","writeOnly":true},"entityId":{"type":"string","description":"NetSuite entity/user ID associated with the token."},"tokenName":{"type":"string","description":"Human-readable name of the NetSuite access token for identification purposes."},"roleId":{"type":"string","description":"NetSuite role ID that determines the permissions for this connection.\n\nThe role controls which records, fields, and operations are accessible.\nMust match the role associated with the access token in NetSuite.\n"},"requestLevelCredentials":{"type":"boolean","default":false,"description":"When true, authentication credentials are sent with each individual API request\nrather than maintaining a persistent session. The connection form sets this to true\nfor token (manual TBA) authentication.\n"},"dataCenterURLs":{"type":"object","description":"NetSuite data center URLs for the account, auto-discovered from the account ID.\nThese are populated by the server after a successful connection.\n","readOnly":true,"properties":{"restDomain":{"type":"string","description":"Base URL for the account's RESTlet and REST API endpoints."},"webservicesDomain":{"type":"string","description":"Base URL for the account's SuiteTalk (SOAP) web services endpoints."},"systemDomain":{"type":"string","description":"Base URL for the account's NetSuite UI/system endpoints."}}},"accountName":{"type":"string","description":"Human-readable NetSuite account name (display purposes only)."},"roleName":{"type":"string","description":"Human-readable name of the NetSuite role (display purposes only)."},"wsdlVersion":{"type":"string","description":"SuiteTalk Web Services WSDL version. The API stores only `latest` or `next`; any other value sent\non create or update is normalized to one of these (the connection form's \"2025.1\" maps to `latest`\nand \"2023.1\" maps to `next`; unrecognized version strings fall back to `latest`). Defaults to\n`latest`, which requires Token-Based Authentication.","enum":["latest","next"],"default":"latest"},"applicationId":{"type":"string","description":"NetSuite application ID from the integration record.\nRequired for some authentication configurations to identify the calling application.\n"},"concurrencyLevel":{"type":"number","description":"General concurrency level for this connection. Controls the overall\nmaximum concurrent requests across all operation types.\nValues above the account's licensed maximum (25 standard, 50 with an Environments license) are silently clamped down.\n","minimum":1,"maximum":50,"default":5},"targetConcurrencyLevel":{"type":"number","description":"Target concurrency level for auto-scaling. The system adjusts concurrency\nbetween 1 and this value based on rate limit and governance feedback.\n","minimum":1,"maximum":50,"default":5},"_iClientId":{"type":"string","format":"objectId","description":"ID of the iClient used for token-based authentication."}}},"Salesforce":{"type":"object","description":"Configuration for Salesforce CRM connections. Used when the connection type is \"salesforce\".\nAuthenticates via OAuth 2.0: oauth2FlowType \"jwtBearerToken\" does server-to-server auth\nthrough a Connected App (recommended for automation), while \"refreshToken\" uses an\ninteractive browser authorization for user-context integrations.","required":["oauth2FlowType","sandbox"],"properties":{"sandbox":{"type":"boolean","description":"Whether this connection targets a Salesforce non-production (sandbox) org. When true,\nauthenticates against test.salesforce.com instead of login.salesforce.com. Must match\nthe actual Salesforce org type or authentication fails. This is the form's\nproduction-vs-sandbox selector (distinct from the connection-level `sandbox` flag).","default":false},"oauth2FlowType":{"type":"string","enum":["jwtBearerToken","refreshToken"],"description":"OAuth 2.0 authentication flow type. Use jwtBearerToken for automated server-to-server\nintegrations (requires a Connected App with a digital certificate and the username field).\nUse refreshToken for integrations that operate in a specific user's context. Defaults to\nrefreshToken when omitted.","default":"refreshToken"},"username":{"type":"string","description":"Salesforce login username (email) of the user whose permissions the integration operates\nunder. Used by the jwtBearerToken flow to identify the subject of the assertion."},"_iClientId":{"type":"string","format":"objectId","description":"Reference to the iClient holding your own Salesforce Connected App's client ID and secret.\nSet this to authenticate through a custom Connected App; omit it to use Celigo's\npre-configured Connected App."},"baseURI":{"type":"string","format":"uri","description":"Salesforce instance URL for this org (e.g. \"https://mycompany.my.salesforce.com\").\nAuto-discovered and populated during OAuth authentication; set it explicitly only to\nforce a specific instance URL."},"bearerToken":{"type":"string","writeOnly":true,"description":"OAuth access token for Salesforce API calls (encrypted at rest; returned masked as\n`\"******\"`). Auto-managed by the system during the OAuth flow; rarely set manually."},"refreshToken":{"type":"string","writeOnly":true,"description":"OAuth refresh token used to mint new access tokens (encrypted at rest; returned masked as\n`\"******\"`). Obtained during the initial browser-based authorization of the refreshToken\nflow; auto-managed by the system."},"packagedOAuth":{"type":"boolean","description":"When true, the connection uses Celigo's pre-configured (packaged) Connected App. Set by\nthe system based on whether a custom `_iClientId` is supplied and the OAuth flow completed;\nnot reliably honored when supplied in the request body."},"scope":{"type":"array","items":{"type":"string"},"description":"OAuth scopes requested during authorization. Scope values are defined by Salesforce — `full`\ngrants complete API access and `refresh_token` enables long-lived refresh tokens. Defaults\nto `full` and `refresh_token` when omitted. A saved empty array is the normal stored state:\nthe connection form does not expose this field, and every save from the connection editor\nresets values written through the API.","default":["full","refresh_token"]},"concurrencyLevel":{"type":"number","description":"Maximum number of concurrent API requests to Salesforce. Salesforce enforces per-org API\nrequest limits, so setting this high consumes the org's API call allocation faster. Values\nabove the licensed ceiling are silently clamped (live-verified: a request for 999 is stored\nas 50).","minimum":1,"maximum":50,"default":5},"targetConcurrencyLevel":{"type":"number","description":"Upper bound for auto-scaling concurrency. When automatic rate-limit recovery is enabled, the\nsystem adjusts concurrency between 1 and this value based on rate-limit feedback from\nSalesforce. Values above the licensed ceiling are silently clamped to 50.","minimum":1,"maximum":50,"default":5}},"if":{"properties":{"oauth2FlowType":{"const":"jwtBearerToken"}},"required":["oauth2FlowType"]},"then":{"required":["username"]}},"FTP":{"type":"object","description":"Configuration for FTP/SFTP/FTPS file transfer connections. Used when the connection type is \"ftp\".\nThe type field selects the transfer protocol, which determines port defaults and available auth methods.","required":["type","hostURI","username"],"properties":{"type":{"type":"string","enum":["ftp","sftp","ftps"],"description":"File transfer protocol type. Prefer sftp for security. Use ftps when the\nserver requires TLS. Only use ftp for legacy systems without encryption support."},"hostURI":{"type":"string","description":"FTP server hostname or IP address.\nDo NOT include the protocol prefix (e.g., use \"ftp.example.com\", not \"sftp://ftp.example.com\").\n"},"port":{"type":"number","description":"Server port number. When omitted, defaults to the standard port for the\nprotocol: 21 for ftp, 22 for sftp, 21 for explicit ftps, and 990 for implicit ftps.\n","minimum":0,"maximum":65535},"username":{"type":"string","description":"Username for server authentication."},"password":{"type":"string","writeOnly":true,"description":"Password for server authentication (encrypted at rest; returned masked as `\"******\"`).\nFor sftp, supply either password or authKey (SSH key)."},"authKey":{"type":"string","description":"SSH private key for SFTP key-based authentication (encrypted at rest; returned masked as `\"******\"`).\nOnly used when type is \"sftp\". Provide the full PEM-encoded private key.\nCan be used alone or alongside a password for two-factor auth.\n","writeOnly":true},"usePassiveMode":{"type":"boolean","description":"When true, uses passive mode for FTP/FTPS data connections.\nIn passive mode, the client initiates both control and data connections,\nwhich works better through firewalls and NAT. Enable for most scenarios.\n","default":true},"enableHostVerification":{"type":"boolean","description":"When true, verifies the server's SSH host key (sftp) or TLS certificate (ftps).\nDisable only for development/testing with self-signed certificates.\n"},"userDirectoryIsRoot":{"type":"boolean","description":"When true, treats the user's home directory as the root directory.\nAll paths are relative to the user's home directory rather than the server root.\n","default":false},"useImplicitFtps":{"type":"boolean","description":"When true, uses implicit FTPS (TLS connection established immediately on port 990).\nWhen false, uses explicit FTPS (starts as FTP on port 21, upgrades to TLS via STARTTLS).\nOnly applies when type is \"ftps\".\n","default":false},"requireSocketReUse":{"type":"boolean","description":"When true, requires the data connection to reuse the same TLS session as the control connection.\nSome FTPS servers require this for security. Only applies to FTPS connections.\n","default":false},"entryParser":{"type":"string","enum":["UNIX","UNIX-TRIM","VMS","WINDOWS","OS/2","OS/400","AS/400","MVS","UNKNOWN-TYPE","NETWARE","MACOS-PETER"],"description":"File listing format parser. Controls how directory listings from the server are interpreted.\nMost servers use UNIX format. Only change this if directory listings appear garbled."},"tradingPartner":{"type":"boolean","readOnly":true,"description":"When true, this connection is designated a B2B/EDI trading partner. Read-only on the connection\nbody — POST/PUT of this field are silently ignored; it is toggled via\n`PUT /connections/{_id}/tradingPartner` (used by the EDI B2B Manager).\n"},"_tpConnectorId":{"type":"string","format":"objectId","description":"Reference to the trading partner connector this connection belongs to. Must reference an\nexisting, published trading partner connector; the API rejects an unknown or unpublished ID\nwith 422 `tpconnector_not_found`.\n"},"concurrencyLevel":{"type":"number","description":"Maximum number of concurrent file transfer operations.\nFTP servers often have low connection limits — keep this value conservative.\nValues above the account's licensed maximum (25 standard, 50 with an Environments license) are silently clamped down.\n","minimum":1,"maximum":50,"default":1},"targetConcurrencyLevel":{"type":"number","description":"Target concurrency level for auto-scaling. The system adjusts concurrency\nbetween 1 and this value based on server response feedback.\n","minimum":1,"maximum":50},"multiThreadCount":{"type":"integer","minimum":1,"description":"Number of parallel transfer threads for this FTP connection. Must be a\nwhole number — decimals are rejected on save. Values above the\naccount's licensed concurrency maximum are silently clamped down. On\nsave the value is mirrored into `userConcurrencyLevel` (and into\n`userTargetConcurrencyLevel` when auto-recovery of rate-limit errors\nis active with `targetConcurrencyLevel` set); clearing it clears the\nmirrors."},"userConcurrencyLevel":{"type":"integer","readOnly":true,"description":"Server-maintained mirror of `multiThreadCount`. Present when\n`multiThreadCount` is set; do not send it — it is recomputed on save."},"userTargetConcurrencyLevel":{"type":"integer","readOnly":true,"description":"Server-maintained mirror of `multiThreadCount` used by concurrency\nauto-scaling. Present only when rate-limit auto-recovery applies;\nrecomputed on save."}},"if":{"properties":{"type":{"const":"sftp"}},"required":["type"]},"then":{"anyOf":[{"required":["password"]},{"required":["authKey"]}]},"else":{"required":["password"]}},"S3":{"type":"object","description":"Configuration for Amazon S3 connections. Used when the connection type is \"s3\". Provides\nupload, download, list, and delete access to S3 buckets. Authenticate with a static IAM\naccess key pair (authType `accesskey`) or by referencing an iClient that holds AWS IAM role\ncredentials (authType `awsIam`). Set pingBucket to an accessible bucket so Celigo can validate\nthe credentials.","properties":{"authType":{"type":"string","enum":["accesskey","awsIam"],"default":"accesskey","description":"Authentication method for the S3 bucket. Defaults to `accesskey` when omitted. Use `awsIam`\nto delegate authentication to an iClient that holds an AWS IAM role instead of embedding a\nstatic key pair."},"accessKeyId":{"type":"string","description":"AWS access key ID for IAM authentication. From an IAM user or role with S3 permissions\n(s3:GetObject, s3:PutObject, s3:ListBucket, etc.). Used when authType is `accesskey`."},"secretAccessKey":{"type":"string","writeOnly":true,"description":"AWS secret access key, paired with accessKeyId. Used when authType is `accesskey`. Encrypted at rest; returned masked as `\"******\"`."},"_iClientId":{"type":"string","format":"objectId","description":"Reference to an iClient that holds AWS IAM role credentials. Required when authType is\n`awsIam`; ignored otherwise."},"pingBucket":{"type":"string","description":"S3 bucket name used for connection health checks (ping). The system performs a HEAD request\non this bucket to verify credentials. Must be a bucket the credentials have access to."},"concurrencyLevel":{"type":"number","description":"Maximum number of concurrent S3 operations. Applies when `autoRecoverRateLimitErrors` is\nfalse; when auto-recover is on (the default) concurrency auto-scales up to\n`targetConcurrencyLevel` and a `concurrencyLevel` sent on its own is reconciled back. Values\nabove the account's licensed maximum are clamped down.","minimum":1,"maximum":50,"default":5},"targetConcurrencyLevel":{"type":"number","description":"Upper bound for auto-scaled concurrency when `autoRecoverRateLimitErrors` is true (the\ndefault). The system raises and lowers concurrency between 1 and this value based on\nrate-limit feedback from S3.","minimum":1,"maximum":50,"default":5}},"if":{"properties":{"authType":{"const":"awsIam"}},"required":["authType"]},"then":{"required":["_iClientId"]},"else":{"required":["accessKeyId","secretAccessKey"]}},"HTTP":{"type":"object","description":"Configuration for HTTP/REST API connections. Used when the connection type is \"http\".\nThis is the most versatile connection type in Celigo, supporting any REST, SOAP, or generic HTTP API.\nThe auth.type field selects the authentication strategy; each type requires specific sub-fields.","required":["baseURI","mediaType"],"properties":{"mediaType":{"type":"string","enum":["xml","json","urlencoded","form-data","plaintext"],"description":"Default content type for outbound HTTP request bodies.\nControls the Content-Type header and how request bodies are serialized."},"successMediaType":{"type":"string","enum":["xml","csv","json","plaintext"],"description":"Expected content type of successful API responses. Controls how response bodies are parsed.\nIf omitted, the system infers the format from the response Content-Type header."},"errorMediaType":{"type":"string","enum":["xml","json","plaintext"],"description":"Expected content type of error responses from the API. Controls how error response bodies are parsed for extracting error messages.\n\nIf omitted, defaults to the same format as successMediaType.\n"},"baseURI":{"type":"string","description":"Base URL for all API requests made through this connection. Required.\n\nAll relative URIs in exports and imports are appended to this base URL.\nMust be an absolute URL (e.g., \"https://api.example.com/v2\").\nHandlebars expressions are supported for dynamic URLs — e.g.\n\"https://{{{connection.settings.subdomain}}}.example.com\".\n\nDo NOT include trailing slashes — relative URIs in exports/imports should start with \"/\".\n"},"disableStrictSSL":{"type":"boolean","description":"When true, disables strict SSL/TLS certificate validation for API requests.\n\nOnly set to true for development/testing with self-signed certificates.\nNEVER disable in production — it removes protection against man-in-the-middle attacks.\n","default":false},"concurrencyLevel":{"type":"number","description":"Maximum number of concurrent HTTP requests this connection can make simultaneously.\n\nHigher values increase throughput but may trigger API rate limits.\nSet this based on the target API's rate limit documentation.\nValues above the account's licensed maximum (25 standard, 50 with an Environments license) are silently clamped down.\nPlatform-managed while `autoRecoverRateLimitErrors` is true — a PUT ignores the value and the recovery machinery moves it toward the target; set `targetConcurrencyLevel` instead.\n","minimum":1,"maximum":50,"default":25},"targetConcurrencyLevel":{"type":"number","description":"Ceiling the platform climbs back to after rate-limit recovery, in steps of 25% of this value.\nWriting it also sets `concurrencyLevel` to the same value and stops any recovery in progress.\n\nHonored only while `autoRecoverRateLimitErrors` is true; ignored, and unset, when it is false.\n","minimum":1,"maximum":50},"retryHeader":{"type":"string","description":"HTTP response header name that contains the retry delay (in seconds) when rate-limited.\n\nDefaults to \"Retry-After\" which is the HTTP standard. Only change this if the API\nuses a non-standard header name for retry-after values.\n","default":"Retry-After"},"formType":{"type":"string","enum":["assistant","rest","http","graph_ql","assistant_graphql"],"description":"Controls the UI form layout for configuring this connection. Determines which\nfields are shown and how they are organized in the Celigo UI.\nFor programmatic creation, http is the most common choice."},"type":{"type":"string","enum":["Amazon-SP-API","Amazon-Hybrid","vendor_central"],"description":"Specific API type for Amazon integrations. Only set this for Amazon connections."},"clientCertificates":{"type":"object","description":"Client certificate configuration for mutual TLS (mTLS) authentication.\n\nUse when the API server requires a client certificate to establish the TLS connection.\nYou can provide either a PEM cert/key pair OR a PFX bundle, but not both.\n","properties":{"cert":{"type":"string","description":"Client certificate in PEM format. Must be paired with the key field.\nCannot be used together with pfx.\n"},"key":{"type":"string","description":"Private key for the client certificate in PEM format (encrypted at rest).","writeOnly":true},"ca":{"type":"string","description":"Certificate Authority (CA) certificate in PEM format.\nUse when the server's certificate is signed by a private CA not in the default trust store.\n"},"passphrase":{"type":"string","description":"Passphrase to decrypt an encrypted private key or PFX bundle (encrypted at rest).","writeOnly":true},"pfx":{"type":"string","description":"PKCS#12 (.pfx/.p12) bundle containing both the certificate and private key (encrypted at rest).\nCannot be used together with cert/key.\n","writeOnly":true}}},"ping":{"type":"object","description":"Connection health check (ping) configuration. Defines how Celigo tests\nwhether this connection is alive and authenticated.\n\nWhen configured, Celigo sends an HTTP request to the specified endpoint and\nevaluates the response to determine connection health. The ping runs when\ntesting the connection in the UI and periodically during flow execution.\n","properties":{"relativeURI":{"type":["string","null"],"description":"Relative URI appended to baseURI for the ping request.\nShould be a lightweight, fast endpoint (e.g., \"/me\", \"/health\", \"/api/v1/status\").\nMay be null when no ping endpoint is configured.\n"},"method":{"type":"string","enum":["GET","POST","PUT","HEAD"],"description":"HTTP method for the ping request. Defaults to GET.\nUse POST only if the health endpoint requires it.\n","default":"GET"},"body":{"type":"string","description":"Request body for the ping request. Only used when method is POST or PUT.\nFor form-data mediaType, must be valid multipart form data.\n"},"successPath":{"type":"string","description":"JSON path or XPath expression to extract a success indicator from the ping response.\nIf the value at this path matches one of the successValues, the ping succeeds.\nIf omitted, any 2xx response is considered successful.\n"},"successValues":{"type":"array","items":{"type":"string"},"description":"Values that indicate a successful ping when found at successPath.\nRequires successPath to be set.\n"},"allowArrayforSuccessPath":{"type":"boolean","description":"When true, the value at successPath can be an array and any matching element counts as success."},"failPath":{"type":"string","description":"JSON path or XPath expression to extract a failure indicator from the ping response.\nIf the value at this path matches one of the failValues, the ping fails even if the HTTP status is 2xx.\n"},"failValues":{"type":"array","items":{"type":"string"},"description":"Values that indicate a failed ping when found at failPath.\nRequires failPath to be set.\n"},"errorPath":{"type":"string","description":"JSON path or XPath expression to extract a human-readable error message from\na failed ping response. The extracted message is shown to the user in the UI.\n"}}},"auth":{"type":"object","description":"Authentication configuration for the API connection.\n\nThe auth.type field selects the authentication strategy. Each type requires\nspecific sub-fields — see the type field description for details.\n","properties":{"type":{"type":"string","enum":["custom","basic","token","oauth","wsse","cookie","digest","oauth1","jwtbearer","awsiam"],"description":"Authentication method for this connection. Determines which auth sub-fields are required.\nEach type has its own credential fields; see the x-enumDescriptions for details."},"failStatusCode":{"type":"number","description":"HTTP status code that indicates an authentication failure (e.g., 401, 403).\nWhen this status code is received, the system triggers re-authentication\nbefore retrying the request.\n"},"failPath":{"type":"string","description":"JSON path or XPath expression to check in response bodies for authentication failure indicators.\nUsed when APIs return 200 OK but embed auth errors in the response body.\n"},"failValues":{"type":"array","items":{"type":"string"},"description":"Values at failPath that indicate an authentication failure.\nRequires failPath to be set.\n"},"failures":{"type":"array","description":"HTTP status codes that indicate an authentication failure and should trigger\nre-authentication. An alternative to failStatusCode that accepts multiple codes.\n","items":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code that signals an authentication failure (e.g., 403)."}}}},"skipFollowAuthorizationHeader":{"type":"boolean","description":"When true, the Authorization header is NOT forwarded on HTTP redirects.\nEnable this for APIs that redirect to a different domain after authentication.\n"},"basic":{"type":"object","description":"Basic authentication credentials. Required when auth.type is \"basic\", \"wsse\", or \"digest\".\n","properties":{"username":{"type":"string","description":"Username for Basic/Digest/WSSE authentication."},"password":{"type":"string","description":"Password for Basic/Digest/WSSE authentication (encrypted at rest).","writeOnly":true}}},"token":{"type":"object","description":"Token-based authentication configuration. Required when auth.type is \"token\".\n\nSupports static API keys/bearer tokens and automatic token refresh flows.\nThe token can be sent in a header (Authorization), query parameter, or request body.\n","properties":{"token":{"type":"string","description":"The API key or bearer token value (encrypted at rest); returned masked as `\"******\"`.\nRequired unless automatic token refresh is configured.\n"},"location":{"type":"string","enum":["url","header","body"],"description":"Where to include the token in outbound requests.\nMost APIs expect header. Use headerName and scheme to control the header format,\nor paramName when sending as a URL query parameter."},"headerName":{"type":"string","description":"HTTP header name for the token when location is \"header\".\nDefaults to \"Authorization\" if omitted.\n"},"scheme":{"type":"string","description":"Token scheme/prefix when sent in a header. Prepended before the token value.\nCommon values: \"Bearer\", \"Token\", \"Basic\".\nExample: scheme \"Bearer\" produces header \"Authorization: Bearer <token>\".\n"},"paramName":{"type":"string","description":"Query parameter name for the token when location is \"url\".\nExample: paramName \"api_key\" produces URL \"?api_key=<token>\".\n"},"refreshMethod":{"type":"string","enum":["GET","POST","PUT"],"description":"HTTP method for automatic token refresh requests.\nRequired when no static token is provided (refresh-based auth flow).\n","default":"POST"},"refreshRelativeURI":{"type":"string","description":"Relative URI (appended to baseURI) for the token refresh endpoint.\nThe system calls this endpoint to obtain a new token when the current one expires.\n"},"refreshBody":{"type":"string","description":"Request body to send with the token refresh request."},"refreshMediaType":{"type":"string","enum":["json","urlencoded","xml","plaintext"],"description":"Content type for the token refresh request body.\n","default":"urlencoded"},"refreshResponseMediaType":{"type":"string","enum":["json","xml"],"description":"Expected content type of the token refresh response."},"refreshTokenPath":{"type":"string","description":"JSON path to extract the new token from the refresh response.\nExample: \"access_token\" or \"data.token\".\n"},"refreshToken":{"type":"string","description":"Refresh token used to obtain a new access token (encrypted at rest).","writeOnly":true},"refreshTokenLocation":{"type":"string","enum":["header","body"],"description":"Where to include the refresh token in refresh requests."},"refreshHeaders":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Header name to send with the token-refresh request."},"value":{"type":"string","description":"Header value to send with the token-refresh request."}}},"description":"Additional headers to include in token refresh requests."},"tokenPaths":{"type":"array","items":{"type":"string"},"description":"JSON paths to extract multiple token values from the refresh response.\nUse when the refresh response contains tokens at different paths that need\nto be stored for subsequent requests.\n"},"revoke":{"type":"object","description":"Token-revocation request configuration. When set, the system calls this\nendpoint to revoke the current token (e.g. when the connection is deleted or\nre-authenticated). Used mainly by pre-built HTTP Connector templates.\n","properties":{"uri":{"type":"string","description":"Absolute URL of the token-revocation endpoint."},"body":{"type":"string","description":"Request body to send with the token-revocation request."},"headers":{"type":"array","description":"Additional headers to include in the token-revocation request.","items":{"type":"object","properties":{"name":{"type":"string","description":"Header name to send with the revocation request."},"value":{"type":"string","description":"Header value to send with the revocation request."}}}}}}}},"oauth":{"$ref":"#/components/schemas/OAuth"},"cookie":{"type":"object","description":"Cookie-based session authentication. Required when auth.type is \"cookie\".\n\nThe system authenticates by sending a request to the login URI, captures the\nsession cookies from the response, and includes them in all subsequent API requests.\n","properties":{"uri":{"type":"string","description":"Login endpoint URI for cookie authentication. Required.\nThe system sends a request to this URI to obtain session cookies.\n"},"body":{"type":"string","description":"Request body for the login request (e.g., JSON with username/password)."},"method":{"type":"string","description":"HTTP method for the login request (typically POST)."},"successStatusCode":{"type":"number","description":"HTTP status code that confirms successful authentication.\nIf the login response returns this status code, the session cookies are captured.\n"}}},"jwt":{"$ref":"#/components/schemas/JWT"}},"if":{"properties":{"type":{"enum":["basic","digest","wsse"]}},"required":["type"]},"then":{"required":["basic"],"properties":{"basic":{"required":["username","password"]}}},"else":{"if":{"properties":{"type":{"const":"token"}},"required":["type"]},"then":{"required":["token"],"properties":{"token":{"required":["token","location"]}}},"else":{"if":{"properties":{"type":{"const":"cookie"}},"required":["type"]},"then":{"required":["cookie"],"properties":{"cookie":{"required":["uri"]}}},"else":{"if":{"properties":{"type":{"const":"jwtbearer"}},"required":["type"]},"then":{"required":["jwt"],"properties":{"jwt":{"required":["signatureMethod","payload"]}},"if":{"properties":{"jwt":{"properties":{"signatureMethod":{"enum":["hmac-sha256","hmac-sha384","hmac-sha512"]}},"required":["signatureMethod"]}},"required":["jwt"]},"then":{"properties":{"jwt":{"required":["signatureMethod","payload","secret"]}}},"else":{"properties":{"jwt":{"required":["signatureMethod","payload","privateKey"]}}}},"else":{"if":{"properties":{"type":{"const":"oauth1"}},"required":["type"]},"then":{"required":["oauth"],"properties":{"oauth":{"required":["oauth1"],"properties":{"oauth1":{"required":["signatureMethod","consumerKey","accessToken"]}}}},"if":{"properties":{"oauth":{"properties":{"oauth1":{"properties":{"signatureMethod":{"enum":["rsa-sha1","rsa-sha256","rsa-sha512"]}},"required":["signatureMethod"]}},"required":["oauth1"]}},"required":["oauth"]},"then":{"properties":{"oauth":{"properties":{"oauth1":{"required":["signatureMethod","consumerKey","accessToken","consumerPrivateKey"]}}}}},"else":{"properties":{"oauth":{"properties":{"oauth1":{"required":["signatureMethod","consumerKey","accessToken","consumerSecret","tokenSecret"]}}}}}}}}}}},"rateLimit":{"type":"object","description":"Rate limiting configuration. Defines how the system detects and handles\nAPI rate limit responses.\n\nWhen rate limiting is detected, the system pauses requests and waits for\nthe retry-after period before resuming. The retryHeader field on the parent\nHTTP object controls which response header contains the wait time.\n","properties":{"failStatusCode":{"type":"number","description":"HTTP status code that indicates the API is rate-limiting requests.\nDefaults to 429 (Too Many Requests) which is the HTTP standard.\nChange only if the API uses a non-standard status code for rate limits.\n","default":429},"failPath":{"type":"string","description":"JSON path or XPath to check in response bodies for rate limit indicators.\nUsed when APIs return 200 OK but embed rate limit errors in the response body.\n"},"failValues":{"type":"array","items":{"type":"string"},"description":"Values at failPath that indicate rate limiting. Requires failPath to be set.\n"},"limit":{"type":"number","minimum":1,"description":"Maximum number of requests per rate-limit window. When set, the connection's\neffective concurrency level must be 1 to ensure proper rate limit enforcement.\n"}}},"headers":{"type":"array","description":"Default HTTP headers included in every request made through this connection.\nUse for API keys in custom headers, content negotiation, or any headers the API requires on all requests.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Header name (e.g., \"X-API-Key\", \"Accept\")."},"value":{"type":"string","description":"Header value. Supports handlebars expressions for dynamic values."}},"required":["name","value"]}},"encrypted":{"type":["object","string"],"description":"Encrypted custom fields for storing sensitive configuration values (API secrets,\nprivate keys, etc.) that don't fit standard auth fields. Sent as an object of\nfield-name/value pairs; values are encrypted at rest. Returned as the masked\nstring `\"******\"` on responses. Field definitions are specified in encryptedFields.\n"},"encryptedFields":{"type":"array","description":"Metadata defining the encrypted custom fields available on this connection.\nEach entry describes a field in the encrypted object — its ID, label, and UI position.\n","items":{"type":"object","properties":{"id":{"type":"string","description":"Field identifier — matches the key in the encrypted object."},"label":{"type":"string","description":"Human-readable label shown in the UI."},"required":{"type":"boolean","default":false,"description":"When true, the custom field defined by this entry is mandatory — a value for it must be supplied before the connection can be saved."},"position":{"type":"number","description":"Display order in the UI form."},"helpText":{"type":"string","description":"Tooltip or help text shown next to the field in the UI."}}}},"unencrypted":{"type":"object","description":"Unencrypted custom fields for non-sensitive configuration values.\nField definitions are specified in unencryptedFields.","properties":{"marketplaceId":{"type":"string","description":"Amazon marketplace identifier for Amazon connection types."},"sellingRegion":{"type":"string","description":"Amazon selling region for the marketplace."},"googleProjectId":{"type":"string","description":"Google Cloud project id for Google Cloud Storage connections\n(assistant `googlecloudstorage`, or a `baseURI` pointing at\n`storage.googleapis.com`)."}}},"unencryptedFields":{"type":"array","description":"Metadata defining the unencrypted custom fields available on this connection.\nEach entry describes a field in the unencrypted object.\n","items":{"type":"object","properties":{"id":{"type":"string","description":"Field identifier — matches the key in the unencrypted object."},"label":{"type":"string","description":"Human-readable label shown in the UI."},"required":{"type":"boolean","default":false,"description":"When true, the custom field defined by this entry is mandatory — a value for it must be supplied before the connection can be saved."},"position":{"type":"number","description":"Display order in the UI form."},"helpText":{"type":"string","description":"Tooltip or help text shown next to the field in the UI."},"type":{"type":"string","description":"Field type hint for the UI (e.g., \"text\", \"select\")."}}}},"_iClientId":{"type":"string","format":"objectId","description":"ID of the iClient used for OAuth authentication."},"_httpConnectorId":{"type":"string","format":"objectId","description":"ID of the HTTP connector template this connection is based on."},"_httpConnectorApiId":{"type":"string","format":"objectId","description":"ID of the HTTP connector API definition (an API within the referenced HTTP connector)."},"_httpConnectorVersionId":{"type":"string","format":"objectId","description":"ID of the HTTP connector version (a version within the referenced HTTP connector)."},"isRest":{"type":"boolean","description":"When true, this HTTP connection uses REST-style semantics (created from a REST connector template)."},"useNewAuthFailSchema":{"type":"boolean","description":"When true, the connection uses the newer authentication-failure detection schema for refresh/retry handling."}},"if":{"properties":{"type":{"enum":["Amazon-SP-API","Amazon-Hybrid","vendor_central"]}},"required":["type"]},"then":{"required":["unencrypted"],"properties":{"unencrypted":{"required":["marketplaceId","sellingRegion"]}}}},"OAuth":{"type":"object","description":"OAuth 2.0 and OAuth 1.0a authentication configuration.\nUsed as a sub-object within HTTP and REST connection auth configurations.\nThe grantType field selects the OAuth 2.0 flow; for legacy OAuth 1.0a APIs, use the oauth1 sub-object.","properties":{"type":{"type":"string","enum":["custom","assistant"],"description":"OAuth configuration mode. Controls whether settings are user-configured or\npre-populated by an application assistant connector."},"grantType":{"type":"string","enum":["authorizecode","clientcredentials","password"],"description":"OAuth 2.0 grant type that determines the authentication flow.\nUse authorizecode for user-context integrations, clientcredentials for\nserver-to-server, and password only when the API does not support other flows.","default":"authorizecode"},"authURI":{"type":"string","description":"OAuth 2.0 authorization endpoint URL.\nRequired for \"authorizecode\" grant type. The user is redirected to this URL\nto authorize the application.\nHandlebars expressions are supported for tenant-specific endpoints — e.g.\n\"https://{{{connection.settings.storeName}}}.myshopify.com/admin/oauth/authorize\".\n"},"tokenURI":{"type":"string","description":"OAuth 2.0 token endpoint URL.\nRequired for \"authorizecode\", \"clientcredentials\", and \"password\" grant types.\nThe system exchanges credentials or authorization codes for access tokens at this URL.\nHandlebars expressions are supported for tenant-specific endpoints.\n"},"skipOauthValidations":{"type":"boolean","description":"When true, skips Celigo's built-in OAuth configuration validation.\nUse when the API has non-standard OAuth requirements that conflict with validation rules.\n","default":false},"scope":{"type":"array","items":{"type":"string"},"description":"OAuth scopes to request during authorization. Controls the level of API access.\nScope values are API-specific (e.g., \"read\", \"write\", \"admin\").\n"},"scopeDelimiter":{"type":"string","description":"Delimiter between multiple scope values. Defaults to a space (\" \") per the\nOAuth 2.0 spec. Some APIs use commas or other delimiters.\n","default":" "},"clientId":{"type":"string","description":"OAuth client ID (application ID) registered with the API provider.\nRequired for all OAuth 2.0 grant types.\n"},"clientSecret":{"type":"string","description":"OAuth client secret (encrypted at rest).\nRequired for \"authorizecode\" and \"clientcredentials\" grant types.\n","writeOnly":true},"username":{"type":"string","description":"Resource owner username. Required when grantType is \"password\".\n"},"password":{"type":"string","description":"Resource owner password (encrypted at rest). Required when grantType is \"password\".\n","writeOnly":true},"clientCredentialsLocation":{"type":"string","enum":["basicauthheader","body"],"description":"Where to send client credentials in token requests. Defaults to basicauthheader\n(HTTP Basic Auth), which is recommended by the OAuth spec. Use body when\nthe API does not support Basic Auth for client credentials.","default":"basicauthheader"},"accessTokenPath":{"type":"string","description":"JSON path to extract the access token from the token endpoint response.\nDefaults to \"access_token\" per the OAuth 2.0 spec.\nChange only if the API returns the token at a non-standard path.\n"},"accessTokenHeaders":{"type":"array","description":"Additional HTTP headers to include in token endpoint requests.\nUse for APIs that require custom headers beyond the standard OAuth parameters.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Header name to send with the token request."},"value":{"type":"string","description":"Header value to send with the token request."}}}},"accessTokenBody":{"type":"string","description":"Additional body content to include in token endpoint requests.\nAppended to the standard OAuth parameters.\n"},"oauth2RedirectUrl":{"type":"string","description":"OAuth 2.0 redirect URI (callback URL) registered with the API provider.\nMust exactly match the redirect URI configured in the OAuth application registration.\n"},"useIClientFields":{"type":"boolean","description":"When true, uses iClient-managed OAuth credentials (clientId/clientSecret)\ninstead of the values in this configuration.\n"},"oauth1":{"type":"object","description":"OAuth 1.0a configuration for legacy APIs that use the older OAuth protocol.\nAlways needs consumerKey and accessToken; HMAC signature methods also need\nconsumerSecret and tokenSecret, while RSA methods need consumerPrivateKey.","properties":{"consumerKey":{"type":"string","description":"OAuth 1.0a consumer key (API key).\nIdentifies the application making the request.\n"},"consumerSecret":{"type":"string","description":"OAuth 1.0a consumer secret (encrypted at rest).\nRequired for HMAC and PLAINTEXT signature methods.\n","writeOnly":true},"accessToken":{"type":"string","description":"OAuth 1.0a access token (encrypted at rest).\nRepresents the user's authorization for the application to access their data.\n","writeOnly":true},"tokenSecret":{"type":"string","description":"OAuth 1.0a token secret (encrypted at rest).\nRequired for HMAC and PLAINTEXT signature methods.\n","writeOnly":true},"signatureMethod":{"type":"string","enum":["hmac-sha1","hmac-sha256","hmac-sha512","rsa-sha1","rsa-sha256","rsa-sha512","plaintext"],"description":"OAuth 1.0a request signing method. HMAC methods require consumerSecret and\ntokenSecret; RSA methods require consumerPrivateKey. PLAINTEXT offers no\ncryptographic signing and should only be used for testing over HTTPS."},"consumerPrivateKey":{"type":"string","description":"RSA private key for OAuth 1.0a RSA signature methods (encrypted at rest).\nRequired when signatureMethod is RSA-SHA1, RSA-SHA256, or RSA-SHA512.\n","writeOnly":true},"realm":{"type":"string","description":"OAuth realm value included in the Authorization header.\nSome APIs require this to identify the authentication domain.\n"}}},"pkceCodeVerifier":{"type":"string","description":"PKCE (Proof Key for Code Exchange) code verifier for enhanced OAuth 2.0 security.\nManaged internally by the system during authorization code flows.\n","writeOnly":true}},"if":{"properties":{"grantType":{"const":"authorizecode"}},"required":["grantType"]},"then":{"required":["authURI","tokenURI"]},"else":{"if":{"properties":{"grantType":{"const":"clientcredentials"}},"required":["grantType"]},"then":{"required":["tokenURI"]},"else":{"if":{"properties":{"grantType":{"const":"password"}},"required":["grantType"]},"then":{"required":["tokenURI","username","password"]}}}},"JWT":{"type":"object","description":"JWT Bearer authentication configuration. Used when auth.type is \"jwtbearer\" on HTTP\nconnections. The connection builds a JWT from payload and headers, signs it using\nsignatureMethod, and sends it as the bearer token. HMAC methods sign with secret;\nRSA/ECDSA/PSS methods sign with privateKey.","properties":{"signatureMethod":{"type":"string","enum":["hmac-sha256","hmac-sha384","hmac-sha512","rsa-sha256","rsa-sha384","rsa-sha512","es256","es384","es512","ps256","ps384","ps512"],"description":"JWT signing method. Required when auth.type is \"jwtbearer\". HMAC methods require\nthe secret field; RSA/ECDSA/PSS methods require the privateKey field."},"payload":{"type":"object","additionalProperties":true,"description":"JWT claims payload (the token body). Required and must be non-empty when auth.type\nis \"jwtbearer\". Keys are JWT claim names (e.g. iss, sub, aud, exp)."},"headers":{"type":"object","additionalProperties":true,"description":"JWT header parameters merged into the generated token header (e.g. kid)."},"secret":{"type":"string","writeOnly":true,"description":"Shared secret for HMAC signature methods (hmac-sha256/hmac-sha512), encrypted at rest.\nRequired when signatureMethod is an HMAC method."},"isSecretBase64Encoded":{"type":"boolean","description":"When true, the secret is base64-decoded before being used to sign."},"privateKey":{"type":"string","writeOnly":true,"description":"PEM-encoded private key for RSA/ECDSA/PSS signature methods, encrypted at rest.\nRequired for any rsa-*, es*, or ps* signature method."},"token":{"type":"string","writeOnly":true,"description":"Pre-generated static JWT to use instead of building one from payload (encrypted at rest).\nUse only when the API issues a long-lived JWT that does not need regeneration."}},"if":{"properties":{"signatureMethod":{"enum":["hmac-sha256","hmac-sha384","hmac-sha512"]}},"required":["signatureMethod"]},"then":{"required":["secret"]},"else":{"if":{"properties":{"signatureMethod":{"enum":["rsa-sha256","rsa-sha384","rsa-sha512","es256","es384","es512","ps256","ps384","ps512"]}},"required":["signatureMethod"]},"then":{"required":["privateKey"]}}},"RDBMS":{"type":"object","description":"Configuration for relational database connections. Used when the connection type is \"rdbms\".\nThe type field selects the database system, which determines the SQL dialect, connection driver,\ndefault port, and which additional sub-fields are required.","required":["type"],"properties":{"type":{"type":"string","enum":["mysql","postgresql","mssql","snowflake","oracle","bigquery","redshift","mariadb","azuresynapse"],"description":"The specific relational database system to connect to. Determines the SQL dialect,\nconnection driver, default port, and which additional sub-fields are required."},"host":{"type":"string","description":"Database server hostname or IP address.\nRequired for all types except BigQuery (which uses Google's API endpoints) and Redshift\n(which is reached through its cluster identifier and region instead of a host).\n\nFor Snowflake, use the account URL format: \"account_identifier.snowflakecomputing.com\".\n"},"port":{"type":"number","description":"Database server port number. If omitted, uses the default port for the database type:\n- MySQL/MariaDB: 3306\n- PostgreSQL: 5432\n- MS SQL/Azure Synapse: 1433\n- Oracle: 1521\n- Snowflake: 443\n- Redshift: 5439\n","minimum":1,"maximum":65535},"database":{"type":"string","description":"Database name to connect to. Required for most types.\n\nFor Oracle, use the serviceName field instead of database.\nFor BigQuery, use bigquery.dataset to specify the target dataset.\n"},"instanceName":{"type":"string","description":"Named instance identifier, used when the server hosts multiple named database instances\n(primarily MS SQL Server). Leave empty for a default instance.\n"},"user":{"type":"string","description":"Database username for authentication.\nRequired for all types except BigQuery (which uses service account auth).\n"},"password":{"type":"string","description":"Database password (encrypted at rest). Required alongside user for password-based auth.","writeOnly":true},"version":{"type":"string","enum":["SQL Server 2008 R2","SQL Server 2012","SQL Server 2014","SQL Server 2016","SQL Server 2017","Azure"],"description":"SQL Server engine version, which selects driver compatibility behavior. Applies to mssql\nand azuresynapse connections only (azuresynapse is always \"Azure\")."},"serviceName":{"type":"string","description":"Oracle service name, supplied instead of the database field for Oracle connections.\nThis is the TNS service name or pluggable database (PDB) service name.\n"},"serverType":{"type":"string","enum":["dedicated","shared","pooled"],"description":"Oracle server connection type. Controls the server process model used for connections."},"concurrencyLevel":{"type":"number","description":"Maximum number of concurrent database connections.\nSet based on the database server's connection limit and available resources.\nValues above the account's licensed maximum (25 standard, 50 with an Environments license) are silently clamped down.\n","minimum":1,"maximum":50,"default":5},"targetConcurrencyLevel":{"type":"number","description":"Target concurrency level for auto-scaling. The system adjusts the number of\nconcurrent connections between 1 and this value based on performance feedback.\n","minimum":1,"maximum":50},"disableStrictSSL":{"type":"boolean","description":"When true, disables strict SSL/TLS certificate validation for the database connection.\nOnly use for development/testing with self-signed certificates.\n","default":false},"snowflake":{"type":"object","description":"Snowflake-specific configuration. Required when type is \"snowflake\".\n","required":["authType"],"properties":{"warehouse":{"type":"string","description":"Snowflake virtual warehouse that provides compute resources for queries.\nMust be a warehouse the user's role can access."},"schema":{"type":"string","description":"Default Snowflake schema. If omitted, queries must fully-qualify table names\n(e.g., DATABASE.SCHEMA.TABLE).\n"},"role":{"type":"string","description":"Snowflake security role to use for the session. Determines which databases,\nschemas, and warehouses are accessible. Defaults to the user's default role.\n"},"authType":{"type":"string","enum":["keyPair"],"description":"Authentication type for Snowflake. keyPair uses RSA key-pair authentication and requires\nthe connection's key field. It is the only type the connection form offers for new\nconnections (password-based auth is no longer available)."}}},"mssql":{"type":"object","description":"Microsoft SQL Server / Azure Synapse-specific configuration.\n","properties":{"authType":{"type":"string","enum":["basic","azure-service-principal"],"description":"Authentication type for MS SQL Server. Defaults to basic (username/password).\nUse azure-service-principal for Azure AD service principal auth via an iClient.","default":"basic"}}},"bigquery":{"type":"object","description":"Google BigQuery-specific configuration. Required when type is \"bigquery\".\nUses Google Cloud service account credentials for authentication.\n","properties":{"projectId":{"type":"string","description":"Google Cloud project ID that contains the BigQuery datasets.\nFound in the Google Cloud Console project settings.\n"},"dataset":{"type":"string","description":"Default BigQuery dataset name.\nQueries will target tables within this dataset unless fully-qualified names are used.\n"},"clientEmail":{"type":"string","format":"email","description":"Google Cloud service account email address.\nThe service account must have BigQuery Data Editor and BigQuery Job User roles.\n"},"privateKey":{"type":"string","description":"Google Cloud service account private key in PEM format (encrypted at rest).\nDownloaded as part of the service account JSON key file.\n","writeOnly":true}}},"redshift":{"type":"object","description":"Amazon Redshift-specific configuration. `region` is always required.\nCredentials depend on `authType`: static access keys in `aws` for\n`accesskey` (the default), or an `awsIam` iClient referenced by\n`rdbms._iClientId` for `awsIam`. The top-level user and password are optional.\n","properties":{"authType":{"type":"string","enum":["accesskey","awsIam"],"default":"accesskey","description":"Selects the Redshift authentication mode; omitting it behaves as\n`accesskey`. With `awsIam`, the platform assumes the iClient's\nrole and requires exactly one of `clusterIdentifier` (provisioned\ncluster) or `workgroupName` (Redshift Serverless) — supplying\nboth or neither fails with 422."},"aws":{"type":"object","description":"Static AWS access keys, used when `authType` is `accesskey` (or\nomitted). Not used with `awsIam` — credentials then come from the\niClient.","properties":{"accessKeyId":{"type":"string","description":"AWS access key ID for IAM authentication."},"secretAccessKey":{"type":"string","description":"AWS secret access key (encrypted at rest).","writeOnly":true}}},"clusterIdentifier":{"type":"string","description":"Redshift cluster identifier, used with the region to reach a\nprovisioned cluster. With `awsIam` auth, exactly one of\n`clusterIdentifier` or `workgroupName` must be set."},"workgroupName":{"type":"string","description":"Redshift Serverless workgroup to connect to instead of a\nprovisioned cluster. Only valid when `authType` is `awsIam` —\nthe API rejects it with `accesskey` auth (422 \"Redshift\nServerless (workgroupName) requires authType=awsIam\") — and is\nmutually exclusive with `clusterIdentifier`."},"region":{"type":"string","description":"AWS region where the Redshift cluster is deployed.\n","enum":["us-east-1","us-east-2","us-west-1","us-west-2","eu-west-1","eu-west-2","eu-west-3","eu-north-1","eu-central-1","ap-southeast-1","ap-southeast-2","ap-northeast-1","ap-northeast-2","ap-south-1","sa-east-1","ca-central-1"]}}},"ssl":{"$ref":"#/components/schemas/SSL"},"options":{"type":"array","description":"Additional database driver connection options as name/value pairs.\nUse for driver-specific settings not covered by the standard fields\n(e.g., connection timeout, charset, application name).\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Option name (driver-specific)."},"value":{"type":"string","description":"Option value."}},"required":["name","value"]}},"key":{"type":"string","writeOnly":true,"description":"RSA private key (PEM-encoded, encrypted at rest). Required when type is `snowflake`\nand `snowflake.authType` is `keyPair`. Not used by any other database type."},"passphrase":{"type":"string","writeOnly":true,"description":"Passphrase that decrypts the RSA private key in the key field, when that key is\nencrypted (encrypted at rest). Used only for `snowflake` with `snowflake.authType`\n`keyPair`; omit when the private key is unencrypted."},"_iClientId":{"type":"string","format":"objectId","description":"Reference to an iClient holding auth credentials, stored inside the\nrdbms object. Required when type is `mssql` and `mssql.authType` is\n`azure-service-principal` (Azure service-principal credentials), and\nwhen type is `redshift` and `redshift.authType` is `awsIam` (an\n`awsIam`-provider iClient holding the role ARN to assume)."}},"if":{"properties":{"type":{"enum":["mysql","postgresql","mariadb","azuresynapse"]}},"required":["type"]},"then":{"required":["host","database","user","password"]},"else":{"if":{"properties":{"type":{"const":"mssql"}},"required":["type"]},"then":{"if":{"properties":{"mssql":{"properties":{"authType":{"const":"azure-service-principal"}},"required":["authType"]}},"required":["mssql"]},"then":{"required":["host","database","version","_iClientId"]},"else":{"required":["host","database","version","user","password"]}},"else":{"if":{"properties":{"type":{"const":"snowflake"}},"required":["type"]},"then":{"required":["host","database","user"],"properties":{"snowflake":{"required":["authType","warehouse"]}},"if":{"properties":{"snowflake":{"properties":{"authType":{"const":"keyPair"}},"required":["authType"]}},"required":["snowflake"]},"then":{"required":["key"]},"else":{"required":["password"]}},"else":{"if":{"properties":{"type":{"const":"oracle"}},"required":["type"]},"then":{"required":["host","user","password"]},"else":{"if":{"properties":{"type":{"const":"bigquery"}},"required":["type"]},"then":{"required":["bigquery"],"properties":{"bigquery":{"required":["projectId","dataset","clientEmail","privateKey"]}}},"else":{"if":{"properties":{"type":{"const":"redshift"}},"required":["type"]},"then":{"required":["database","user","redshift"],"properties":{"redshift":{"required":["region"]}},"if":{"required":["redshift"],"properties":{"redshift":{"required":["authType"],"properties":{"authType":{"const":"awsIam"}}}}},"then":{"required":["_iClientId"],"properties":{"redshift":{"oneOf":[{"required":["clusterIdentifier"],"not":{"required":["workgroupName"]}},{"required":["workgroupName"],"not":{"required":["clusterIdentifier"]}}]}}},"else":{"properties":{"redshift":{"required":["aws","clusterIdentifier"],"not":{"required":["workgroupName"]},"properties":{"aws":{"required":["accessKeyId","secretAccessKey"]}}}}}}}}}}}},"MongoDB":{"type":"object","description":"Configuration for MongoDB connections. Used when the connection type is \"mongodb\". Supports\nstandalone instances, replica sets, and MongoDB Atlas clusters. For a replica set, list all\nmember addresses in host and set replicaSet to the set name; authSource selects the\nauthentication database when it differs from the target database.","required":["host","username","password"],"properties":{"host":{"type":"array","minItems":1,"items":{"type":"string"},"description":"MongoDB server addresses. An array of one or more host:port strings.\n\n- Standalone: [\"mongodb.example.com:27017\"]\n- Replica set: [\"rs1.example.com:27017\", \"rs2.example.com:27017\", \"rs3.example.com:27017\"]\n- MongoDB Atlas: [\"cluster0-shard-00-00.abc.mongodb.net:27017\", ...]\n\nInclude the port number with each host. Default MongoDB port is 27017.\n"},"database":{"type":"string","description":"Target MongoDB database name.\nAll operations (reads/writes) target collections within this database.\nOptional on the connection — when omitted, set the database per operation instead.\n"},"username":{"type":"string","description":"MongoDB username for authentication."},"password":{"type":"string","writeOnly":true,"description":"MongoDB password. Write-only — accepted on create/update and returned masked as `\"******\"`."},"replicaSet":{"type":"string","description":"MongoDB replica set name. Set this when connecting to a replica set so the driver can\ndiscover all members and handle failover. For MongoDB Atlas, this is typically\n\"atlas-xxxxxx-shard-0\".\n"},"ssl":{"type":"boolean","description":"When true, connects to MongoDB over TLS/SSL.\nRequired for MongoDB Atlas and recommended for all production deployments.\n","default":false},"authSource":{"type":"string","description":"MongoDB authentication database — the database where the user credentials are stored.\nDefaults to the value of the database field. Set to \"admin\" if the user was created\nin the admin database (common for shared MongoDB deployments and Atlas).\n"},"concurrencyLevel":{"type":"number","description":"Maximum number of concurrent MongoDB operations.\nValues above the account's licensed maximum (25 standard, 50 with an Environments license) are silently clamped down.\n","minimum":1,"maximum":50,"default":5},"targetConcurrencyLevel":{"type":"number","description":"Target concurrency level for auto-scaling. The system adjusts concurrency\nbetween 1 and this value based on rate limit feedback.\n\nOnly relevant when autoRecoverRateLimitErrors is enabled on the connection.\n","minimum":1,"maximum":50,"default":5}}},"AS2":{"type":"object","description":"AS2 (Applicability Statement 2) connection configuration for EDI","required":["as2Id","partnerId","partnerStationInfo","userStationInfo"],"properties":{"as2Id":{"type":"string","description":"AS2 identifier for this station. Trading partners use this as the \"To\"\nidentifier when sending documents, and integrator.io uses it as the \"From\"\nidentifier when sending documents to partners.\n\nMust be unique across ALL integrator.io users so inbound documents route\ncorrectly. Set on creation and cannot be changed afterward — a PUT with a\ndifferent value is ignored. Use a distinct identifier per environment\n(e.g. production vs. non-production). If omitted, a unique value is auto-generated.\n"},"partnerId":{"type":"string","description":"Trading partner's AS2 identifier — the partner's \"From\" identifier on documents they\nsend and the \"To\" identifier integrator.io uses when sending to them. Set on creation\nand cannot be changed afterward; a PUT with a different value is ignored."},"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading partner connector this AS2 connection was provisioned from during partner onboarding. Omit for a standalone AS2 connection."},"contentBasedFlowRouter":{"type":"object","description":"Routes inbound documents to different flows based on message content, for connections\nshared across multiple flows. Omit unless a routing script is required.","properties":{"function":{"type":"string","description":"Name of the exported function in the routing script that returns the target flow."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the routing function named in `function`."}}},"partnerStationInfo":{"type":"object","description":"Partner (remote) station configuration, used on the IMPORT side — controls how messages\nare sent TO the trading partner.","required":["as2URI","signing","encryptionType"],"properties":{"as2URI":{"type":"string","format":"uri","description":"Partner's AS2 endpoint that integrator.io posts outbound messages to."},"mdn":{"type":"object","description":"Settings for the MDN (Message Disposition Notification) receipt the partner returns for outbound messages.","required":["mdnSigning"],"properties":{"mdnURL":{"type":"string","format":"uri","description":"Endpoint the partner posts asynchronous MDNs to. Set only when the partner returns MDNs asynchronously rather than on the same connection."},"signatureProtocol":{"type":"string","enum":["pkcs7-signature"],"description":"Signature protocol the partner uses on returned MDNs."},"mdnSigning":{"type":"string","enum":["NONE","SHA1","MD5","SHA256"],"description":"Hash algorithm used to verify the signature on MDNs the partner returns. Defaults to NONE when the partner does not sign MDNs."},"verifyMDNSignature":{"type":"boolean","description":"When true, verifies the signature on MDNs returned by the partner. Requires unencrypted.partnerCertificate."}}},"auth":{"type":"object","description":"Authentication for posting messages to the partner's AS2 endpoint. Omit (or set\ntype to none) when the endpoint is unauthenticated.","properties":{"type":{"type":"string","enum":["basic","token","none"],"description":"Authentication scheme for the partner endpoint. Set the matching basic or token sub-object for basic or token."},"failStatusCode":{"type":"number","description":"HTTP status code in the partner's response that signals an authentication failure, triggering a token refresh or error."},"failPath":{"type":"string","description":"Path in the partner's JSON response body checked against failValues to detect an authentication failure."},"failValues":{"type":"array","items":{"type":"string"},"description":"Values at failPath that indicate an authentication failure. Requires failPath."},"basic":{"type":"object","description":"Credentials for basic authentication. Present when type is basic.","properties":{"username":{"type":"string","description":"Username for basic authentication."},"password":{"type":"string","writeOnly":true,"description":"Password for basic authentication. Masked as \"******\" in GET responses."}}},"token":{"type":"object","description":"Token configuration. Present when type is token.","properties":{"token":{"type":"string","writeOnly":true,"description":"Bearer/access token sent with each request to the partner. Masked as \"******\" in GET responses."},"location":{"type":"string","enum":["header","url","body"],"description":"Where to place the token in the outbound request."},"headerName":{"type":"string","description":"Header name carrying the token when location is header. Defaults to Authorization."},"scheme":{"type":"string","enum":["bearer","mac","oauth","none"],"description":"Authorization scheme prepended to the token when location is header."},"paramName":{"type":"string","description":"Query parameter name carrying the token when location is url."},"refreshToken":{"type":"string","writeOnly":true,"description":"Token used to obtain a new access token when the current one expires. Masked as \"******\" in GET responses."},"refreshRelativeURI":{"type":"string","description":"Endpoint, relative to the partner's host, called to refresh the token."},"refreshMethod":{"type":"string","enum":["GET","POST","PUT"],"description":"HTTP method for the token-refresh request."},"refreshMediaType":{"type":"string","enum":["json","urlencoded","xml"],"description":"Content type of the token-refresh request body."},"refreshBody":{"type":"string","description":"Request body sent with the token-refresh request, used when refreshMethod is POST or PUT."},"refreshTokenPath":{"type":"string","description":"Path in the refresh response body where the new access token is found."},"refreshHeaders":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Header name to send with the token-refresh request."},"value":{"type":"string","description":"Header value to send with the token-refresh request."}}},"description":"Headers sent with the token-refresh request."}}}}},"rateLimit":{"type":"object","description":"Throttles requests to the partner endpoint when it enforces a non-standard rate limit.","properties":{"failStatusCode":{"type":"number","description":"HTTP status code in the partner's response that indicates rate limiting."},"failPath":{"type":"string","description":"Path in the partner's JSON response body checked against failValues to detect rate limiting."},"failValues":{"type":"array","description":"Values at failPath that indicate rate limiting. Requires failPath.","items":{"type":"string"}},"limit":{"type":"number","minimum":1,"description":"Wait time in milliseconds between requests to the partner endpoint."}}},"SMIMEVersion":{"type":"string","enum":["v2","v3"],"description":"S/MIME version applied to outbound messages. Not exposed in the connection form."},"signing":{"type":"string","enum":["NONE","SHA1","MD5","SHA256"],"description":"Hash algorithm for signing messages sent to the partner. Any value other than NONE requires encrypted.userPrivateKey and unencrypted.userPublicKey."},"encryptionType":{"type":"string","enum":["NONE","DES","RC2","3DES","AES128","AES256"],"description":"Cipher for encrypting messages sent to the partner. Any value other than NONE requires unencrypted.partnerCertificate."},"encoding":{"type":"string","enum":["base64","binary"],"description":"Content transfer encoding for outbound messages. Shown in the form only when encryptionType is not NONE."},"signatureEncoding":{"type":"string","enum":["base64","binary"],"description":"Encoding applied to the digital signature on outbound messages."}}},"userStationInfo":{"type":"object","description":"User (local) station configuration, used on the EXPORT side — controls how inbound\nmessages from the partner are processed.","required":["signing","encryptionType"],"properties":{"mdn":{"type":"object","description":"Settings for the MDN receipt this station returns to the partner for inbound messages.","required":["mdnSigning"],"properties":{"mdnURL":{"type":"string","anyOf":[{"format":"uri"},{"const":""}],"description":"Partner's endpoint this station posts asynchronous MDNs to. Set only when\nthe partner requires asynchronous MDNs; stored as an empty string otherwise."},"signatureProtocol":{"type":"string","enum":["pkcs7-signature"],"description":"Signature protocol applied to MDNs this station returns."},"mdnSigning":{"type":"string","enum":["NONE","SHA1","MD5","SHA256"],"description":"Hash algorithm used to sign MDNs this station returns to the partner."},"mdnEncoding":{"type":"string","enum":["base64","binary"],"description":"Encoding applied to MDNs this station returns."}}},"signing":{"type":"string","enum":["NONE","SHA1","MD5","SHA256"],"description":"Hash algorithm for verifying signatures on inbound messages. Any value other than NONE requires unencrypted.partnerCertificate."},"encryptionType":{"type":"string","enum":["NONE","DES","RC2","3DES","AES128","AES256"],"description":"Cipher for decrypting inbound messages. Any value other than NONE requires encrypted.userPrivateKey and unencrypted.userPublicKey."},"encoding":{"type":"string","enum":["base64","binary"],"description":"Content transfer encoding expected on inbound messages. Shown in the form only when encryptionType is not NONE."},"compressed":{"type":"boolean","description":"When true, message content is compressed. Not exposed in the connection form.","default":false}}},"encrypted":{"type":"object","description":"Encrypted-at-rest key material for this station's own identity. Supply when this station\nsigns outbound messages or decrypts inbound messages; integrator.io injects an\nauto-generated self-signed key otherwise.","properties":{"userPrivateKey":{"type":"string","writeOnly":true,"description":"PEM-encoded X.509 private key for this station, used to sign outbound messages and\ndecrypt inbound ones. Masked as \"******\" in GET responses. Required when\npartnerStationInfo.signing is not NONE or userStationInfo.encryptionType is not NONE."}}},"unencrypted":{"type":"object","description":"Public certificate material for this station and the partner. integrator.io injects an\nauto-generated self-signed certificate for this station when none is supplied.","properties":{"userPublicKey":{"type":"string","description":"PEM-encoded X.509 public certificate for this station, paired with\nencrypted.userPrivateKey. Required when partnerStationInfo.signing is not NONE or\nuserStationInfo.encryptionType is not NONE."},"partnerCertificate":{"type":"string","description":"PEM-encoded X.509 certificate for the trading partner, used to encrypt outbound\nmessages and verify inbound signatures and MDNs. Required when\npartnerStationInfo.encryptionType is not NONE, userStationInfo.signing is not NONE,\nor partnerStationInfo.mdn.verifyMDNSignature is true."}}},"concurrencyLevel":{"type":"number","description":"Maximum number of messages processed concurrently for this connection. The default of 5\nis safe for most partners. Values above the account's licensed maximum (25 standard, 50\nwith an Environments license) are silently clamped down.","minimum":1,"maximum":50,"default":5},"targetConcurrencyLevel":{"type":"number","description":"Concurrency ceiling integrator.io scales up to when auto-recovering from rate-limit\nerrors. Only applies when autoRecoverRateLimitErrors is true on the connection.","minimum":1,"maximum":50},"preventCanonicalization":{"type":"boolean","description":"When true, message content is not canonicalized before signing or encryption. Enable only when a partner reports signature-verification failures caused by canonicalization.","default":false}}},"Filesystem":{"type":"object","description":"Configuration for filesystem connections. Used when the connection type is \"filesystem\".\nProvides access to directories on a Celigo on-premise Agent's host or mounted network\ndrives, so a connection-level `_agentId` is required — cloud-only deployments cannot use\nthis type. File-based PGP encryption/decryption is configured through the connection-level\n`pgp` object.","properties":{"ping":{"type":"object","description":"Connection health-check configuration. integrator.io reads this directory to verify the agent can reach the filesystem.","properties":{"directoryPath":{"type":"string","description":"Absolute directory path the agent checks to confirm filesystem access. Accepts a POSIX path,\na Windows drive path, or a UNC share. Leave unset to skip the directory check during ping."}}},"concurrencyLevel":{"type":"number","description":"Maximum number of files processed concurrently. Cannot exceed `targetConcurrencyLevel`; a higher\nvalue is silently clamped down to it. Values above the account's licensed maximum (25 standard,\n50 with an Environments license) are also clamped down.","minimum":1,"maximum":50,"default":1},"targetConcurrencyLevel":{"type":"number","description":"Upper bound for auto-scaling. The agent scales concurrency between 1 and this value based on\nthroughput, so it also caps `concurrencyLevel`. Values above the account's licensed maximum\n(25 standard, 50 with an Environments license) are silently clamped down.","minimum":1,"maximum":50,"default":1}}},"MCP":{"type":"object","description":"Configuration for MCP (Model Context Protocol) connections. Used when the connection type\nis \"mcp\". Lets Celigo call tools exposed by an external MCP server over HTTP. Authentication\ngoes through the http sub-object, which reuses the HTTP auth config but accepts only the\ntoken, oauth, and custom auth types.","required":["serverURL","http"],"properties":{"protocol":{"type":"string","enum":["http"],"default":"http","readOnly":true,"description":"Transport protocol for communicating with the MCP server.\nCurrently only \"http\" is supported; the server sets this automatically and ignores other values.\n"},"serverURL":{"type":"string","format":"uri","description":"MCP server endpoint URL. Must be a valid absolute URL\n(e.g. \"https://mcp-server.example.com/mcp\")."},"timeout":{"type":"number","default":600000,"description":"Request timeout in milliseconds for MCP tool invocations.\nIf the MCP server does not respond within this time, the request fails.\nDefaults to 600000 (10 minutes) when omitted.\n"},"allowedTools":{"type":"array","description":"Optional allowlist of MCP tool names that this connection may invoke.\nWhen set, only tools in this list can be called. When omitted or empty,\nall tools exposed by the MCP server are available.\n","items":{"type":"string"}},"http":{"type":"object","required":["auth"],"description":"HTTP transport configuration for the MCP connection, including authentication and headers.\n","properties":{"_iClientId":{"type":"string","format":"objectId","description":"Reference to an OAuth iClient for OAuth-based MCP authentication.\nRequired when http.auth.type is \"oauth\".\n"},"auth":{"type":"object","required":["type"],"description":"Authentication configuration for the MCP connection. The auth.type field selects the\nstrategy. MCP supports only token, oauth, and custom — other HTTP auth types\n(basic, wsse, cookie, jwt, etc.) are rejected with a 422.\n","properties":{"type":{"type":"string","enum":["token","oauth","custom"],"description":"Authentication method for the MCP server. Determines which auth sub-fields apply."},"token":{"$ref":"#/components/schemas/token"},"oauth":{"$ref":"#/components/schemas/OAuth"}}},"headers":{"type":"array","description":"Default HTTP headers included in every request to the MCP server.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Header name."},"value":{"type":"string","description":"Header value. Supports handlebars expressions for dynamic values."}},"required":["name","value"]}},"unencrypted":{"type":"object","description":"Unencrypted custom fields for non-sensitive MCP configuration."},"encrypted":{"type":["object","string"],"description":"Encrypted custom fields for sensitive MCP configuration. Sent as an object;\nreturned as the masked string `\"******\"` on responses."}},"if":{"properties":{"auth":{"properties":{"type":{"const":"token"}},"required":["type"]}},"required":["auth"]},"then":{"properties":{"auth":{"required":["token"],"properties":{"token":{"required":["token"]}}}}},"else":{"if":{"properties":{"auth":{"properties":{"type":{"const":"oauth"}},"required":["type"]}},"required":["auth"]},"then":{"required":["_iClientId"]}}}}},"token":{"type":"object","description":"Token-based authentication configuration. Required when auth.type is \"token\".\n\nSupports static API keys/bearer tokens and automatic token refresh flows.\nThe token can be sent in a header (Authorization), query parameter, or request body.\n","properties":{"token":{"type":"string","description":"The API key or bearer token value (encrypted at rest); returned masked as `\"******\"`.\nRequired unless automatic token refresh is configured.\n"},"location":{"type":"string","enum":["url","header","body"],"description":"Where to include the token in outbound requests.\nMost APIs expect header. Use headerName and scheme to control the header format,\nor paramName when sending as a URL query parameter."},"headerName":{"type":"string","description":"HTTP header name for the token when location is \"header\".\nDefaults to \"Authorization\" if omitted.\n"},"scheme":{"type":"string","description":"Token scheme/prefix when sent in a header. Prepended before the token value.\nCommon values: \"Bearer\", \"Token\", \"Basic\".\nExample: scheme \"Bearer\" produces header \"Authorization: Bearer <token>\".\n"},"paramName":{"type":"string","description":"Query parameter name for the token when location is \"url\".\nExample: paramName \"api_key\" produces URL \"?api_key=<token>\".\n"},"refreshMethod":{"type":"string","enum":["GET","POST","PUT"],"description":"HTTP method for automatic token refresh requests.\nRequired when no static token is provided (refresh-based auth flow).\n","default":"POST"},"refreshRelativeURI":{"type":"string","description":"Relative URI (appended to baseURI) for the token refresh endpoint.\nThe system calls this endpoint to obtain a new token when the current one expires.\n"},"refreshBody":{"type":"string","description":"Request body to send with the token refresh request."},"refreshMediaType":{"type":"string","enum":["json","urlencoded","xml","plaintext"],"description":"Content type for the token refresh request body.\n","default":"urlencoded"},"refreshResponseMediaType":{"type":"string","enum":["json","xml"],"description":"Expected content type of the token refresh response."},"refreshTokenPath":{"type":"string","description":"JSON path to extract the new token from the refresh response.\nExample: \"access_token\" or \"data.token\".\n"},"refreshToken":{"type":"string","description":"Refresh token used to obtain a new access token (encrypted at rest).","writeOnly":true},"refreshTokenLocation":{"type":"string","enum":["header","body"],"description":"Where to include the refresh token in refresh requests."},"refreshHeaders":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Header name to send with the token-refresh request."},"value":{"type":"string","description":"Header value to send with the token-refresh request."}}},"description":"Additional headers to include in token refresh requests."},"tokenPaths":{"type":"array","items":{"type":"string"},"description":"JSON paths to extract multiple token values from the refresh response.\nUse when the refresh response contains tokens at different paths that need\nto be stored for subsequent requests.\n"},"revoke":{"type":"object","description":"Token-revocation request configuration. When set, the system calls this\nendpoint to revoke the current token (e.g. when the connection is deleted or\nre-authenticated). Used mainly by pre-built HTTP Connector templates.\n","properties":{"uri":{"type":"string","description":"Absolute URL of the token-revocation endpoint."},"body":{"type":"string","description":"Request body to send with the token-revocation request."},"headers":{"type":"array","description":"Additional headers to include in the token-revocation request.","items":{"type":"object","properties":{"name":{"type":"string","description":"Header name to send with the revocation request."},"value":{"type":"string","description":"Header value to send with the revocation request."}}}}}}}},"DynamoDB":{"type":"object","description":"Configuration for Amazon DynamoDB connections. Used when the connection type is \"dynamodb\".\nAuthenticates with AWS IAM access keys, which must grant the DynamoDB actions the integration\nuses (GetItem, PutItem, Query, Scan, etc.). The AWS region is resolved per table at request\ntime, so no region is stored on the connection.","required":["aws"],"properties":{"aws":{"type":"object","description":"AWS IAM access-key credentials used to sign DynamoDB requests.","required":["accessKeyId","secretAccessKey"],"properties":{"accessKeyId":{"type":"string","description":"Access key ID of the IAM user or role whose policy grants the required DynamoDB actions."},"secretAccessKey":{"type":"string","description":"Secret access key paired with `accessKeyId`. Encrypted at rest and returned masked as `\"******\"`.","writeOnly":true}}},"concurrencyLevel":{"type":"number","description":"Maximum number of DynamoDB requests this connection runs at once.\n\nValues are silently clamped into the licensed range — below 1 is raised to 1, and above the\naccount maximum (25 standard, 50 with an Environments license) is lowered to that maximum.","minimum":1,"maximum":50,"default":5},"targetConcurrencyLevel":{"type":"number","description":"Concurrency ceiling for auto-scaling. The system adjusts the active concurrency between 1\nand this value based on rate-limit feedback. Only applied when `autoRecoverRateLimitErrors`\nis enabled on the connection. Clamped the same way as `concurrencyLevel`.","minimum":1,"maximum":50,"default":5}}},"JDBC":{"type":"object","description":"Configuration for JDBC (Java Database Connectivity) connections. Used when the connection\ntype is \"jdbc\". Provides access through Java JDBC drivers to databases not covered by the\nrdbms type — such as NetSuite SuiteAnalytics, Databricks, DB2, and Workday. The driver runs\non a Celigo on-premise Agent, so most jdbc connections need a connection-level _agentId.","required":["type"],"properties":{"type":{"type":"string","enum":["agent","netsuitejdbc","databricks","oracle:thin","sqlserver","activedirectory","db2","workday"],"description":"JDBC driver/connection type. Selects which JDBC driver is used and determines\nthe required fields. Most JDBC connections require a Celigo on-premise Agent."},"version":{"type":"string","description":"JDBC driver version. Used for driver compatibility when multiple versions are available."},"host":{"type":"string","description":"Database server hostname or IP address.\nFor NetSuite JDBC, use the SuiteAnalytics Connect hostname\n(e.g., \"account-id.connect.api.netsuite.com\").\n"},"port":{"type":"number","description":"Database server port number. Default varies by driver type."},"database":{"type":"string","description":"Database or catalog name. For Oracle, use the serviceName field instead.\n"},"user":{"type":"string","description":"Database username for authentication."},"password":{"type":"string","description":"Database password (encrypted at rest).","writeOnly":true},"serviceName":{"type":"string","description":"Oracle service name. Used instead of the database field for Oracle JDBC connections.\nThis is the TNS service name or pluggable database (PDB) service name.\n"},"authType":{"type":"string","enum":["customjdbc","wallet"],"description":"Authentication method for the JDBC connection. Defaults to customjdbc (username/password).\nUse wallet for Oracle Wallet authentication with the wallet field."},"wallet":{"type":"string","description":"Oracle Wallet file contents (encrypted at rest).\nRequired when authType is \"wallet\". Contains the auto-login wallet (cwallet.sso)\nwith encrypted credentials for passwordless Oracle authentication.\n","writeOnly":true},"driverPath":{"type":"string","description":"File path to the JDBC driver JAR on the Celigo Agent.\nRequired when type is \"agent\" (generic JDBC).\nThe driver must be deployed on the agent before creating the connection.\n"},"properties":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"JDBC connection property name (driver-specific)."},"value":{"type":"string","description":"JDBC connection property value."}}},"description":"Additional JDBC connection properties as name/value pairs.\nThese are passed directly to the JDBC driver as connection properties.\nUse for driver-specific settings like SSL mode, connection timeout,\napplication name, etc.\n"},"concurrencyLevel":{"type":"number","description":"Maximum number of concurrent database connections.\nJDBC connections often run through a single Agent, so keep this value\nconservative to avoid overwhelming the Agent or database.\nValues above the account's licensed maximum (25 standard, 50 with an Environments license) are silently clamped down.\n","minimum":1,"maximum":50,"default":5},"targetConcurrencyLevel":{"type":"number","description":"Target concurrency level for auto-scaling. The system adjusts the number of\nconcurrent connections between 1 and this value based on performance feedback.\n","minimum":1,"maximum":50,"default":5}},"if":{"properties":{"type":{"const":"db2"}},"required":["type"]},"then":{"required":["host","user","password","database","port","properties"]},"else":{"if":{"properties":{"type":{"const":"oracle:thin"}},"required":["type"]},"then":{"required":["user","password","authType"],"if":{"properties":{"authType":{"const":"wallet"}},"required":["authType"]},"then":{"required":["wallet","serviceName"]},"else":{"required":["host"]}},"else":{"if":{"properties":{"type":{"const":"activedirectory"}},"required":["type"]},"then":{"required":["host","port","user","password","properties"]},"else":{"if":{"properties":{"type":{"const":"netsuitejdbc"}},"required":["type"]},"then":{"required":["host","user","properties"]},"else":{"if":{"properties":{"type":{"const":"agent"}},"required":["type"]},"then":{"required":["host","driverPath"]},"else":{"if":{"properties":{"type":{"const":"workday"}},"required":["type"]},"then":{"required":["host","user","password","properties"]},"else":{"if":{"properties":{"type":{"const":"databricks"}},"required":["type"]},"then":{"required":["host"]}}}}}}}},"VAN":{"type":"object","description":"Configuration for VAN (Value-Added Network) connections. Used when the connection type is \"van\".\nA VAN connection exchanges EDI documents through a managed network mailbox; outbound documents are\nposted to the mailbox and inbound documents are picked up from it, addressed by the connection's AS2 identifier.","properties":{"as2Id":{"type":"string","description":"AS2 identifier for this VAN station — the \"From\" identifier on outbound documents and the \"To\"\nidentifier on inbound. Must be unique across all integrator.io VAN connections; a duplicate fails\nthe create. Set once when the connection is created and cannot be changed afterward (writes on update\nare ignored). Omit it to have the network assign one automatically."},"mailboxId":{"type":"number","readOnly":true,"description":"Numeric mailbox identifier assigned by the VAN provider when the connection is provisioned.\nServer-assigned and not writable — values sent on create or update are ignored."},"contentBasedFlowRouter":{"type":"object","description":"Routes inbound documents to different flows based on message content, for a connection shared across\nmultiple flows. The named function in the referenced script inspects each incoming document and returns\nthe flow to run. Omit to disable content-based routing.","properties":{"function":{"type":"string","description":"Name of the exported function in the referenced script that returns the target flow for each inbound document."},"_scriptId":{"type":"string","format":"objectId","description":"ID of the script resource that contains the routing function. Must reference an existing script; an unknown ID fails the request with 422 `invalid_ref`."}}}}},"Wrapper":{"type":"object","description":"Configuration for Wrapper connections. Used when the connection type is \"wrapper\". A wrapper\nis a fully custom connector implemented in server-side JavaScript on a Celigo Stack\n(referenced by _stackId) — use it when the target API needs\nnon-standard authentication or logic that HTTP connections can't express. Connection-specific\nvalues go in the encrypted/unencrypted fields, which the wrapper code reads at runtime.","required":["pingFunction"],"properties":{"pingFunction":{"type":"string","description":"Name of the JavaScript function on the Stack that tests connection health, invoked when\n\"Test Connection\" is clicked. It should verify the credentials are valid and the target\nsystem is reachable."},"unencrypted":{"type":"object","description":"Unencrypted custom fields for non-sensitive configuration values.\nThese values are accessible to the wrapper code on the Stack at runtime.\nField definitions are specified in unencryptedFields.\n"},"unencryptedFields":{"type":"array","description":"Metadata defining the unencrypted custom fields on this connection.","items":{"type":"object","properties":{"id":{"type":"string","description":"Field identifier — matches the key in the unencrypted object."},"label":{"type":"string","description":"Human-readable label shown in the UI."},"required":{"type":"boolean","default":false,"description":"When true, the custom field defined by this entry is mandatory — a value for it must be supplied before the connection can be saved."},"position":{"type":"number","description":"Display order in the UI form."},"helpText":{"type":"string","description":"Tooltip text shown next to the field in the UI."},"type":{"type":"string","description":"Optional, legacy field-type hint. The wrapper form ignores it — it always renders these\ncustom fields as text inputs — so it is rarely set."}}}},"encrypted":{"type":["object","string"],"description":"Encrypted custom fields for sensitive configuration values (API keys, passwords, etc.).\nSent as an object of field-name/value pairs; values are encrypted at rest and decrypted\nonly on the Stack at runtime. Returned as the masked string `\"******\"` on responses.\nField definitions are specified in encryptedFields.\n"},"encryptedFields":{"type":"array","description":"Metadata defining the encrypted custom fields on this connection.","items":{"type":"object","properties":{"id":{"type":"string","description":"Field identifier — matches the key in the encrypted object."},"label":{"type":"string","description":"Human-readable label shown in the UI."},"required":{"type":"boolean","default":false,"description":"When true, the custom field defined by this entry is mandatory — a value for it must be supplied before the connection can be saved."},"position":{"type":"number","description":"Display order in the UI form."},"helpText":{"type":"string","description":"Tooltip text shown next to the field in the UI."},"type":{"type":"string","description":"Optional, legacy field-type hint. The wrapper form ignores it — it always renders these\ncustom fields as text inputs (masked) — so it is rarely set."}}}},"_stackId":{"type":"string","format":"objectId","description":"Reference to the Celigo Stack whose server-side JavaScript implements this connection."},"concurrencyLevel":{"type":"number","description":"Maximum number of concurrent operations through this wrapper connection.\nValues above the account's licensed maximum (25 standard, 50 with an Environments license) are silently clamped down.\n","minimum":1,"maximum":50,"default":5},"targetConcurrencyLevel":{"type":"number","description":"Target concurrency level for auto-scaling. The system adjusts concurrency\nbetween 1 and this value based on performance feedback.\n","minimum":1,"maximum":50}}},"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/tools/{_id}/connections":{"get":{"summary":"List connections a tool depends on","operationId":"listToolConnections","tags":["Tools"],"description":"Returns the full Connection resources the tool references — both directly\n(via `_connectionId` fields on its steps) and transitively through\ndescendant resources (inner tools, lookups, imports, exports).\n\nUseful for discovering what systems a tool talks to before cloning, moving,\nor evaluating the blast radius of a connection change. For the full\ndependency tree (imports, exports, nested tools), use\n`GET /v1/tools/{_id}/descendants` instead.","parameters":[{"name":"_id","in":"path","required":true,"description":"Tool id.","schema":{"type":"string","format":"objectId"}}],"responses":{"200":{"description":"Array of full Connection objects referenced by the tool and its\ndescendants. Empty array when no connections are referenced.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Connection"}}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
```

## List resources a tool depends on, grouped by type

> Returns the full dependency tree of a tool as three arrays: the\
> \`imports\`, \`exports\`, and nested \`tools\` it references directly or\
> transitively. Each entry is the complete resource document, so the\
> caller doesn't need to fan out individual GETs.\
> \
> Pair with \`GET /v1/tools/{\_id}/connections\` to enumerate the full\
> resource and connection footprint in two calls.

````json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"tags":[{"name":"Tools","description":"Tools are reusable processing units within integrations that encapsulate input\ntransformation, conditional routing, output mapping, and data enrichment logic behind\nan input/output contract. They can be referenced from flows, APIs, AI agents, MCP\nservers, and other tools to promote modularity and reuse.\n\n## Tool schema\n\n{% openapi-schemas spec=\"tool\" schemas=\"Tool\" 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-2"},"netsuite_da":{"$ref":"#/components/schemas/NetsuiteDistributed"},"rdbms":{"$ref":"#/components/schemas/Rdbms"},"s3":{"$ref":"#/components/schemas/S3-2"},"wrapper":{"$ref":"#/components/schemas/Wrapper-2"},"salesforce":{"$ref":"#/components/schemas/Salesforce-2"},"tool":{"$ref":"#/components/schemas/Tool-2"},"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-2":{"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-2":{"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-2":{"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-2":{"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-2":{"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"]}}}},"Export":{"type":"object","required":["_id","name","adaptorType","createdAt","lastModified"],"description":"Export object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ExportBase"},{"$ref":"#/components/schemas/ResourceResponse"},{"$ref":"#/components/schemas/IAResourceResponse"},{"type":"object","properties":{"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"apim":{"$ref":"#/components/schemas/APIM"},"apiIdentifier":{"type":"string","readOnly":true,"description":"API identifier assigned to this export."},"asynchronous":{"type":"boolean","readOnly":true,"description":"Server-managed execution-mode flag set on creation; client values are ignored."},"__linkedLookupCacheIds":{"type":"array","readOnly":true,"description":"Lookup caches linked to this export, managed by the platform.","items":{"type":"string","format":"objectId"}},"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Reference to the source resource this export was created from."},"_templateId":{"type":"string","format":"objectId","readOnly":true,"description":"Template this export was created from."},"draft":{"type":"boolean","readOnly":true,"description":"When true, this export is in draft state and has not been confirmed."},"draftExpiresAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the draft version of this export expires."},"debugUntil":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp until which debug logging is enabled for this export."}}}]},"ExportBase":{"type":"object","description":"Writable export fields shared by the request and response schemas.","properties":{"name":{"type":"string","maxLength":100,"description":"Display name for the export, shown in the flow builder, job history, and error logs.\nDescriptive, unique names indicating the data source and purpose make large accounts easier to manage."},"description":{"type":["string","null"],"description":"Free-text summary of what the export retrieves and why. Shown in the UI and available\nto AI agents for context; has no effect on execution.","maxLength":5120},"_connectionId":{"format":"objectId","type":["string","null"],"description":"Connection this export uses to reach the source system. The connection's type must be\ncompatible with the export's `adaptorType` (e.g. an `HTTPExport` needs an `http` connection;\n`NetSuiteExport` and `NetSuiteHTTPExport` both need a `netsuite` connection).\nServer-required unless `type` is `webhook` or `simple` (those receive data instead of fetching it)."},"adaptorType":{"type":"string","description":"Selects the adaptor technology that executes this export, which determines the compatible\nconnection types and which adaptor-specific configuration object must also be supplied\n(e.g. set `salesforce` when using `SalesforceExport`).","enum":["HTTPExport","FTPExport","AS2Export","S3Export","NetSuiteExport","NetSuiteHTTPExport","SalesforceExport","JDBCExport","RDBMSExport","MongodbExport","DynamodbExport","WrapperExport","SimpleExport","WebhookExport","FileSystemExport","RESTExport"]},"nsDomainType":{"type":"string","enum":["suitetalk","restlet"],"description":"Selects which NetSuite REST host a `NetSuiteHTTPExport` calls. The server derives the\nfull base URL from the connection's NetSuite account\n(`https://<account>.suitetalk.api.netsuite.com` or `https://<account>.restlets.api.netsuite.com`)\nand signs each request with the connection's token-based credentials, so `http.relativeURI`\ncarries the complete path starting at `/services/rest/...` or `/app/site/hosting/restlet.nl`.\nIgnored on other adaptor types."},"type":{"type":["string","null"],"description":"Operational mode of the export. When omitted, the export retrieves all available records —\nthe standard batch mode, which is also the right choice for parsing structured files\n(CSV/XML/JSON) into records. Each mode requires its matching configuration object\n(e.g. a `delta` object when `type` is `delta`); use `blob` only to transfer raw files\nwithout parsing their contents.","enum":["webhook","test","delta","once","tranlinedelta","simple","blob","distributed","stream","all"]},"pageSize":{"type":["integer","null"],"description":"Number of records per page streamed to downstream flow steps when the export is the\nflow's source step. Pages are additionally capped at 5 MB regardless of record count, so\nvery large records may produce smaller pages. The server does not validate the value.\nA lookup (`isLookup: true`) runs to completion and hands every result to the incoming\nrecord as one response, capped at 5 MB in total, so the value does not change how many\nresults a lookup returns; a result set larger than that belongs in a standalone export\nstep. In a test run or preview the value also caps the records fetched (default 20).","default":20},"dataURITemplate":{"type":"string","description":"Handlebars template that builds a link back to each record in the source application's UI\n(e.g. `https://my.salesforce.com/lightning/r/Contact/{{record.Id}}/view`). The resolved\nURL is stored with error records in job history so users can jump straight to the record."},"traceKeyTemplate":{"type":["string","null"],"description":"Handlebars template that overrides how each record's unique trace key is generated, used\nto track records through the flow and match retries to prior errors (e.g.\n`{{join \"_\" record.customerId record.orderId}}`). When omitted, the system picks a unique\nfield automatically. If a transform reshapes the data first, omit the `record.` prefix.\nTrace keys are capped at 256 characters; a longer key is stored truncated from the middle,\nkeeping the beginning and end of the value."},"skipRetries":{"type":"boolean","description":"When true, the platform does not retain the source data needed to retry failed records,\nreducing stored data at the cost of being unable to reprocess errors from this export.\nShown as \"Do not store retry data\" in the UI. Defaults to false."},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"isLookup":{"type":"boolean","description":"When true, the export runs as a mid-flow lookup: it executes once per incoming record,\nusing the input record's fields to parameterize the request, and passes the results to\nsubsequent steps. When false, the export runs as a standalone data source. A lookup's\nresults are not paged through the flow: every page the adaptor fetches is merged into one\nresponse for the record, up to 5 MB, so `pageSize` does not limit or extend what a lookup\nreturns. A lookup that must return more than that is better modeled as a standalone\nexport step."},"groupByFields":{"$ref":"#/components/schemas/GroupBy"},"delta":{"$ref":"#/components/schemas/Delta"},"test":{"$ref":"#/components/schemas/Test"},"once":{"$ref":"#/components/schemas/Once"},"webhook":{"$ref":"#/components/schemas/Webhook"},"simple":{"$ref":"#/components/schemas/Simple"},"distributed":{"$ref":"#/components/schemas/Distributed"},"cdc":{"$ref":"#/components/schemas/Cdc"},"filesystem":{"$ref":"#/components/schemas/FileSystem-2"},"http":{"$ref":"#/components/schemas/Http-2"},"file":{"$ref":"#/components/schemas/File-2"},"salesforce":{"$ref":"#/components/schemas/Salesforce-3"},"as2":{"$ref":"#/components/schemas/AS2-2"},"dynamodb":{"$ref":"#/components/schemas/DynamoDB-2"},"ftp":{"$ref":"#/components/schemas/FTP-2"},"jdbc":{"$ref":"#/components/schemas/JDBC-2"},"mongodb":{"$ref":"#/components/schemas/MongoDB-2"},"netsuite":{"$ref":"#/components/schemas/NetSuite-3"},"rdbms":{"$ref":"#/components/schemas/RDBMS-2"},"s3":{"$ref":"#/components/schemas/S3-3"},"wrapper":{"$ref":"#/components/schemas/Wrapper-3"},"parsers":{"$ref":"#/components/schemas/Parsers"},"filter":{"description":"Filter applied to records immediately after they are retrieved from the source.\nRecords that match continue through the flow; records that don't are silently dropped.\nFilter expressions reference the exported record's own fields.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"inputFilter":{"description":"Filter applied to incoming records before a lookup export queries the external system.\nOnly matching input records trigger lookup calls; non-matching records pass through the\nstep unenriched, which cuts unnecessary API traffic. Only relevant when `isLookup` is true.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"mappings":{"description":"Field mappings applied to each incoming record before a lookup HTTP request is made,\nreshaping the upstream record into the structure the lookup target API expects. Only\nsupported when `isLookup` is true and `adaptorType` is `HTTPExport` or\n`NetSuiteHTTPExport`; do not set it on source exports or other lookup adaptors.","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"transform":{"description":"Transformation that reshapes records before they leave this step — source exports\ntransform the retrieved records, lookup exports transform the lookup results. Commonly\nused to flatten nested lookup responses (e.g. `results[0].id` → `id`); merging results\nback into source records is handled separately by the flow's response mapping.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the export lifecycle for\ntransformations, validation, and filtering beyond what configuration alone can express.","properties":{"preSavePage":{"type":"object","description":"Hook that runs after each page of records is retrieved from the source but before it\nis sent to downstream steps. Commonly used to flatten structures, drop unwanted\nrecords, or add computed fields.","properties":{"function":{"type":"string","description":"Function to invoke within the referenced script."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments."},"configuration":{"type":"object","description":"Static parameters passed to the hook function at runtime, letting one script be\nreused across exports with different settings."}}}}},"settingsForm":{"$ref":"#/components/schemas/Form"},"settings":{"$ref":"#/components/schemas/Settings"},"mockOutput":{"$ref":"#/components/schemas/MockOutput"},"_ediProfileId":{"type":"string","format":"objectId","description":"EDI profile this export uses to parse incoming X12 or EDIFACT documents into structured\nJSON — it supplies the envelope qualifiers, delimiters, version, and validation rules.\nSet it when the export parses or validates EDI files; omit it otherwise. Accepted on the\nfile-reading exports — FTP, AS2, S3 and HTTP file mode (`http.type: \"file\"`)."},"_postParseListenerId":{"type":"string","format":"objectId","description":"Webhook export invoked once per file after EDI parsing, on both success and failure\n(errors are included in the payload when parsing fails). Primarily used to send\nfunctional acknowledgements (997/999) to trading partners. Accepted on the file-reading\nexports that parse EDI — FTP, AS2, S3 and HTTP file mode (`http.type: \"file\"`)."},"externalId":{"type":["string","null"],"description":"External identifier for correlating the export with a record in another system."},"_integrationId":{"type":["string","null"],"format":"objectId","description":"Integration this export belongs to."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this export was created from, set when the export is part of an installed integration app."},"unencrypted":{"type":"object","description":"Custom configuration values stored without encryption and returned in API responses."},"useTechAdaptorForm":{"type":"boolean","description":"When true, the UI presents the full technical adaptor form for this export instead of\nthe simplified assistant form."},"rawData":{"type":"string","description":"Key referencing the raw sample payload captured for preview and testing."},"preSave":{"$ref":"#/components/schemas/PreSave"},"assistant":{"type":"string","description":"Identifier for the connector assistant used to configure this export."},"assistantMetadata":{"type":["object","string"],"additionalProperties":true,"description":"Metadata associated with the connector assistant configuration."},"sampleData":{"type":["string","object","array","null"],"description":"Sample data payload used for previewing and testing the export."},"sampleHeaders":{"type":"array","description":"Sample HTTP headers used for previewing and testing the export.","items":{"type":"object","properties":{"name":{"type":"string","description":"Header name."},"value":{"type":"string","description":"Header value."}}}},"sampleQueryParams":{"type":"array","description":"Sample query parameters used for previewing and testing the export.","items":{"type":"object","properties":{"name":{"type":"string","description":"Query parameter name."},"value":{"type":"string","description":"Query parameter value."}}}}}},"GroupBy":{"type":"array","description":"Specifies which fields to use for grouping records in the export results. When configured, records with\nthe same values in these fields will be grouped together and treated as a single record by downstream\nsteps in your flow.\n\nFor example:\n- Group sales orders by customer ID to process all orders for each customer together\n- Group journal entries by accounting period to consolidate related transactions\n- Group inventory items by location to process inventory by warehouse\n\nWhen grouping is used, the export's page size determines the maximum number of groups per page, not individual\nrecords. Note that effective grouping typically requires that records with the same group field values appear\ntogether in the export data.\n","items":{"type":"string"}},"Delta":{"type":"object","description":"Configures incremental exports that retrieve only records created or modified since the last\nsuccessful run. Required when the export's type is \"delta\"; omit for other export types.\nWhen no cutoff is supplied, the platform-managed last-successful-run timestamp (exposed as\n{{lastExportDateTime}}) is the lower bound — the first run behaves like a full export, and\nafter a failed run the next run reuses the last successful timestamp so changed records are\nnot missed.","properties":{"dateField":{"type":"string","description":"Record timestamp field(s) compared against the last successful run time to identify\nchanged records. Accepts a single field or multiple comma-separated fields, processed\nsequentially — useful when different operations update different timestamp fields.\nIf the flow's own downstream steps update the exported records, add export criteria\nthat exclude already-processed records (or use a creation-time field for process-once\nflows) so each run doesn't re-export what the previous run wrote. Not supported on\nHTTP exports: embed {{lastExportDateTime}} in the relativeURI or body instead;\nincluding dateField there makes the configuration invalid. For Salesforce this\nfield is required and defaults to the standard timestamp fields (LastModifiedDate,\nCreatedDate, SystemModstamp, LastActivityDate, LastViewedDate, LastReferencedDate) plus\nany custom timestamp fields."},"dateFormat":{"type":"string","description":"Moment.js format string applied to the cutoff timestamp, including {{lastExportDateTime}}\nwhen used in HTTP requests. Leave unset unless the source system requires a non-ISO8601\nformat; ISO 8601 is used by default. Date-only formats truncate the time portion,\nwidening the filter window."},"lagOffset":{"type":"integer","description":"Buffer in milliseconds subtracted from the last successful run timestamp, creating an\noverlapping window that catches records still propagating when the previous run executed.\nSet it when records created or modified near the run time are occasionally skipped due to\nreplication or indexing delays. Keep it as low as possible — larger values reprocess\nredundant records. A negative value shifts the window forward (look-ahead) instead of back."},"startDate":{"type":["string","null"],"format":"date-time","description":"Explicit lower-bound cutoff for the first run, overriding the default of starting from the\nbeginning of time. Subsequent runs use the platform-managed last-successful-run timestamp.\nSet it to backfill from a specific point rather than exporting all history."}}},"Test":{"type":"object","description":"Configures test exports that cap the number of records retrieved, for safely developing and\nvalidating against small data samples. Required when the export's type is \"test\"; omit for\nother export types. A test export behaves like a standard export in every other way (filters,\npagination, processing) and stores no state between runs.","properties":{"limit":{"type":"integer","default":1,"description":"Caps the total records a test run processes, counted on top-level records before\noneToMany processing; the maximum exists to prevent accidentally processing large\ndatasets during development. The cap applies across pages — processing stops once the\nlimit is reached regardless of pageSize. When transitioning to production, leave this in\nplace and change the export's type field; the limit then no longer applies.","minimum":1,"maximum":100}}},"Once":{"type":"object","description":"Configures flag-based exports that process each record exactly once. Required when the\nexport's type is \"once\"; omit for other export types. Each run retrieves records where the\ntracking boolean field is false, then sets that field to true after successful processing so\nlater runs skip them; if a run fails, the flags are left unchanged and the records are\nretried automatically on the next run.","properties":{"booleanField":{"type":"string","description":"API field name of the boolean/checkbox in the source system that tracks processed\nrecords: the export selects only records where this field is false, then sets it to true\nin batches after each successfully processed page. The field must be writeable by the\nexport's connection. Ensure no other process updates the same field — use a separate\nflag per export process — or records may be skipped unexpectedly."}}},"Webhook":{"type":"object","description":"Configuration for webhook listeners that receive data through incoming HTTP requests. Required when the export's `type` is `webhook`; omit for other export types. The platform generates a unique endpoint URL, validates each incoming request using the configured `verify` method, passes the payload to subsequent flow steps, and returns a configurable HTTP response to the caller.","properties":{"provider":{"type":"string","description":"Source application sending the webhook, used to pre-configure security settings and payload parsing for that platform. Use `custom` (the default when omitted) for unlisted providers or when you need full manual control over the security configuration. Provider-specific choices may require credentials (tokens, keys) mandated by that platform.","enum":["github","shopify","travis","travis-org","slack","dropbox","onfleet","helpscout","errorception","box","stripe","aha","jira","pagerduty","postmark","mailchimp","intercom","activecampaign","segment","recurly","shipwire","surveymonkey","parseur","mailparser-io","hubspot","integrator-extension","custom","sapariba","happyreturns","typeform"]},"verify":{"type":"string","description":"Verification method applied to every incoming request before processing; required for all webhook exports. Each method needs companion fields: `hmac` requires `key`, `algorithm`, `encoding`, and `header` (except on connector-backed webhooks, where the connector definition supplies them); `token` requires `token`, with `tokenLocation` defaulting to `body` and selecting which location-specific field applies; `basic` requires `username` and `password`; `secret_url` requires `token`. Prefer `hmac` when the source system supports it — it is the most secure option; `secret_url` relies only on URL obscurity and suits non-sensitive data or testing.","enum":["token","hmac","basic","secret_url"]},"token":{"type":"string","description":"Shared secret used when `verify` is `token` or `secret_url`. For `token` verification, the value found at `path` in each request must exactly match (case- and whitespace-sensitive) or the request is rejected with a 401. For `secret_url`, the token is embedded in the webhook URL to create a hard-to-guess endpoint — generate a random, high-entropy value and treat it as a sensitive credential."},"algorithm":{"type":"string","description":"Hashing algorithm used to validate HMAC signatures when `verify` is `hmac`. Must match the algorithm the webhook sender uses — a mismatch causes every request to be rejected. Use `sha256` unless the provider explicitly requires another value.","enum":["sha1","sha256","sha384","sha512"]},"encoding":{"type":"string","description":"Encoding of the HMAC signature value when `verify` is `hmac`. Must match the encoding the webhook sender uses — a mismatch causes requests to be rejected even when the signature is otherwise correct.","enum":["hex","base64"]},"key":{"type":"string","description":"Secret used to validate signatures when `verify` is `hmac`. The system computes a signature of the request body using this key and the configured `algorithm`, then compares it with the signature sent in the request header named by `header`. Treat it as a highly sensitive credential — never expose it in repositories or logs."},"header":{"type":"string","description":"Name of the request header that carries the HMAC signature when `verify` is `hmac`. Must match the header name the webhook sender uses (header names are case-insensitive); requests without this header are rejected with a 401. Signature prefixes in the header value (such as `sha256=`) are handled automatically."},"tokenLocation":{"type":"string","enum":["body","header","queryParam"],"default":"body","description":"Where the verification token is found when `verify` is `token`. Each location uses a different companion field: `body` uses `path`, `header` uses `tokenHeaderName` (and `tokenHeaderScheme`), `queryParam` uses `tokenQueryParamName`."},"path":{"type":"string","description":"JSON path into the request body holding the verification token when `verify` is `token` and `tokenLocation` is `body` (e.g. `meta.token`). The value at this path must exactly match `token` or the request is rejected."},"tokenHeaderName":{"type":"string","description":"Request header carrying the verification token when `verify` is `token` and `tokenLocation` is `header`."},"tokenHeaderScheme":{"type":"string","enum":["bearer","custom","none"],"default":"bearer","description":"Scheme prefixing the token in the header when `tokenLocation` is `header`."},"customTokenScheme":{"type":"string","description":"Custom header scheme prefix used when `tokenHeaderScheme` is `custom`."},"tokenQueryParamName":{"type":"string","description":"Query parameter carrying the verification token when `verify` is `token` and `tokenLocation` is `queryParam`."},"_httpConnectorId":{"type":"string","format":"objectId","description":"HTTP connector backing this listener when the webhook is provided by an assistant/connector (e.g. Slack); set by the platform for connector-backed listeners."},"requestMediaType":{"type":"string","enum":["json","xml","csv","urlencoded","plaintext"],"description":"Overrides how the incoming request body is parsed when the provider sends a non-standard content type. When omitted, the body is parsed by its Content-Type header."},"pathToRecords":{"type":"string","description":"JSON path into the incoming payload to the array of records to emit; when omitted, the whole payload is emitted as a single record."},"includeParentData":{"type":"boolean","description":"When true and `pathToRecords` is set, each emitted record also carries the surrounding parent fields from the payload."},"username":{"type":"string","description":"Username half of the credentials when `verify` is `basic`. Incoming requests must include an `Authorization: Basic` header carrying the base64-encoded `username:password` pair. Use only over HTTPS to prevent credential interception."},"password":{"type":"string","description":"Password half of the credentials when `verify` is `basic`, validated together with `username` from the request's `Authorization` header. Use a strong, unique value and treat it as a sensitive credential."},"successStatusCode":{"type":"integer","description":"HTTP status code returned to the caller after successful processing; must be a valid 2xx code. The default 204 returns no response body and causes `successBody` to be ignored — set 200 or 202 when the caller needs a response body or expects a specific code.","default":204},"successBody":{"type":"string","description":"Response body returned to the caller after successful processing; ignored when `successStatusCode` is 204. Content type is set by `successMediaType`, and the value can be static text or structured JSON/XML, including handlebars expressions for dynamic values."},"successMediaType":{"type":"string","description":"Sets the Content-Type header on successful webhook responses. Only takes effect when a `successBody` is returned (a status code other than 204), and must match the actual format of that body.","default":"json","enum":["json","xml","plaintext"]},"successResponseHeaders":{"type":"array","description":"Custom headers added to successful webhook responses — for example CORS headers or correlation IDs. Headers defined here take precedence over automatically set headers such as Content-Type, and values support handlebars expressions for dynamic content.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the header to set on successful webhook responses; headers defined here take precedence over automatically set headers such as Content-Type."},"value":{"type":"string","description":"Value sent for the header; supports handlebars expressions for dynamic content."}}}},"challengeResponseHeaders":{"type":"array","description":"Custom headers returned for webhook subscription verification (challenge) requests, which providers send before delivering real events. Required header values vary by provider — consult the provider's documentation, since incorrect challenge headers can prevent the subscription from being verified.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the header to set on challenge (subscription verification) responses."},"value":{"type":"string","description":"Value sent for the header; consult the provider's documentation for required challenge header values."}}}},"challengeSuccessBody":{"type":"string","description":"Response body returned for webhook subscription verification (challenge) requests. Many providers require echoing back a challenge value from the request, which handlebars expressions can access — for example `{{hub.challenge}}` (Facebook/Instagram) or `{\"challenge\":\"{{challenge}}\"}` (Slack). An incorrect challenge response prevents the subscription from being verified."},"challengeSuccessStatusCode":{"type":"integer","description":"HTTP status code returned for webhook subscription verification (challenge) requests. Most providers expect the default 200; change it only when the provider's verification explicitly requires a different code.","default":200},"challengeSuccessMediaType":{"type":"string","description":"Sets the Content-Type header on challenge responses. Must match both the format of `challengeSuccessBody` and the provider's requirements — for example, Slack expects `json` while Facebook/Instagram verification expects `plaintext`.","default":"json","enum":["json","xml","plaintext"]}},"if":{"properties":{"verify":{"const":"hmac"}},"required":["verify"],"not":{"required":["_httpConnectorId"]}},"then":{"required":["key","algorithm","encoding","header"]},"else":{"if":{"properties":{"verify":{"const":"token"}},"required":["verify"]},"then":{"required":["token"],"if":{"properties":{"tokenLocation":{"const":"body"}},"required":["tokenLocation"]},"then":{"required":["path"]},"else":{"if":{"properties":{"tokenLocation":{"const":"header"}},"required":["tokenLocation"]},"then":{"required":["tokenHeaderName"]},"else":{"if":{"properties":{"tokenLocation":{"const":"queryParam"}},"required":["tokenLocation"]},"then":{"required":["tokenQueryParamName"]},"else":{"required":["path"]}}}},"else":{"if":{"properties":{"verify":{"const":"basic"}},"required":["verify"]},"then":{"required":["username","password"]},"else":{"if":{"properties":{"verify":{"const":"secret_url"}},"required":["verify"]},"then":{"required":["token"]}}}}},"Simple":{"type":"object","description":"Configuration for `simple` (data-loader) exports — exports that accept manually uploaded\nfiles through the data-loader UI instead of fetching data from a connection. Relevant only\nwhen the export's `type` is `simple`; the uploaded content is parsed using the same file\nsettings as file-based exports.","properties":{"file":{"$ref":"#/components/schemas/File-2"}}},"File-2":{"type":"object","description":"Controls how files are parsed, filtered, and processed across all file-based exports\n(FTP/SFTP, Amazon S3, simple file uploads, and other file sources). The type field selects\nthe file format and determines which format-specific object (csv, json, xlsx, xml, or\nfileDefinition) must be configured; the output field selects whether files are parsed into\nrecords, transferred as blobs, or listed as metadata only. The filter object selectively\nskips files before processing.","properties":{"encoding":{"type":"string","description":"Character encoding used to read and parse file content. If the encoding is unknown, try\nutf8 first (the default), then win1252 for Western-language files with garbled\ncharacters; consider the geographic origin of the data when selecting.","enum":["utf8","win1252","utf-16le","gb18030","macroman","iso88591","shiftjis"]},"type":{"type":"string","description":"Format of the files being processed; determines which format-specific configuration\nobject (csv, json, xlsx, xml, or fileDefinition) must be populated — other format\nobjects are ignored. Required for all file-based exports except blob exports (export\ntype \"blob\" or output \"blobKeys\").","enum":["csv","json","xlsx","xml","filedefinition"]},"output":{"type":"string","description":"Processing mode for retrieved files: parse contents into records, transfer files as\nunparsed blobs, or return only file metadata. Determines what data is passed to\nsubsequent flow steps.","enum":["records","metadata","blobKeys"]},"skipDelete":{"type":"boolean","description":"When true, source files remain on the file server after processing; when false (the\ndefault), files are deleted after successful processing. Files that fail processing are\nnever deleted, regardless of this setting. Enable retention when files must be processed\nby other flows or kept for compliance."},"compressionFormat":{"type":"string","description":"Compression format of incoming files, which are decompressed before any other processing\n(parsing, filtering). Set this only when the source always delivers compressed files —\nif a file marked as compressed is not actually compressed, processing fails. Leave unset\nwhen files arrive uncompressed or only sometimes compressed.","enum":["gzip","zip"]},"purgeInternalBackup":{"type":"boolean","description":"When true, Celigo keeps no internal backup copies of files processed by this export;\nwhen false (the default), copies are retained for your account's retention period and\nare available for reprocessing or troubleshooting. Applies only to this export and only\nto Celigo's internal copies — source files are governed by skipDelete. Enable for highly\nsensitive data or zero-retention policies; without backups, recovery may require\nre-obtaining files from the source system."},"decrypt":{"type":"string","description":"Decryption applied to incoming files before any other processing; decryption runs before\ndecompression, and a decryption failure fails the file's processing entirely. The\nconnection must already be configured with the private key (and passphrase, if\napplicable) matching the public key used to encrypt the files. Only PGP/GPG encryption\nis currently supported.","enum":["pgp"]},"batchSize":{"type":["integer","null"],"description":"Number of files retrieved per batch; if a batch fails, the whole batch is retried\n(stored as `null` when not configured). Use\nlower values (10-50) for large files to reduce timeout and memory pressure, and higher\nvalues for many small files to improve throughput. Controls file retrieval only — record\npaging is governed by the export's pageSize.","maximum":1000},"sortByFields":{"type":"array","description":"Sorts the records parsed from each file before they are processed, establishing a\ndeterministic processing order (for example, chronological or priority-based). Sorting\nhappens after parsing but before any filtering or grouping, and does not modify the\nsource files. Sorting by the same fields used in groupByFields improves grouping\nperformance.","items":{"type":"object","properties":{"field":{"type":"string","description":"Record field to sort by; use dot notation for nested properties (e.g.\ncustomer.name). Field names are case-sensitive."},"descending":{"type":"boolean","description":"When true, sorts this field in descending order (newest/highest first); when false\nor omitted, sorts ascending. Directions can be mixed across fields in a\nmulti-field sort."}}}},"groupByFields":{"$ref":"#/components/schemas/GroupBy"},"groupEmptyValues":{"type":"boolean","description":"When true, records whose groupByFields values are empty are still grouped together rather\nthan each forming its own group; when false (the default), empty-keyed records are not\ngrouped. Only relevant when groupByFields is set."},"csv":{"type":"object","description":"Parsing settings for delimiter-separated text files (CSV, TSV, pipe-delimited, and\nsimilar). Configure when type is \"csv\".\n\nRequired when type is csv.","properties":{"columnDelimiter":{"type":"string","description":"Character sequence separating fields within each row; comma when omitted. Use \"\\t\"\nfor tab-delimited files; European-locale exports often use semicolons. An incorrect\ndelimiter is the most common cause of parsing failures."},"rowDelimiter":{"type":"string","description":"Character sequence marking the end of each record; auto-detected from file content\nwhen omitted. Set explicitly (\"\\n\", \"\\r\\n\", or \"\\r\") only when auto-detection fails,\nsuch as for files with mixed line endings — an incorrect value merges or splits\nrecords."},"hasHeaderRow":{"type":"boolean","description":"When true (the default), the first row is read as field names rather than data, and\nthose names are available in mappings. When false, every row is treated as a data\nrecord and fields are referenced by position."},"trimSpaces":{"type":"boolean","description":"When true, removes leading and trailing whitespace from every field value during\nparsing; when false (the default), whitespace is preserved exactly as in the source.\nHeader row values are always trimmed regardless of this setting, and spaces between\nwords are never affected."},"rowsToSkip":{"type":"integer","description":"Number of rows at the top of the file to ignore before parsing begins — useful for\nreport titles, timestamps, or other metadata above the data. The header row (when\nhasHeaderRow is true) is expected after the skipped rows."},"disableQuoteAndStripEnclosingQuotes":{"type":"boolean","description":"When true, disables CSV quote processing: quotes are treated as literal characters,\nenclosing quotes are stripped, and delimiters inside quoted text split the field.\nWhen false (the default), standard RFC 4180 quoting applies and quoted fields\nprotect embedded delimiters. Enable only for files with non-standard or malformed\nquoting; review this setting first when field counts vary unexpectedly between rows."},"keyColumns":{"type":"array","description":"Field names whose values together identify a record's group when consecutive rows\nshare the same key — used to merge multi-line records (e.g. an order header repeated\nacross line-item rows) into a single record. Leave empty for one record per row; the\ncolumns must exist in the parsed header.","items":{"type":"string"}}}},"json":{"type":"object","description":"Parsing settings for JSON files. Configure when type is \"json\". resourcePath locates the\narray of records when they are nested inside a container object; malformed JSON fails\nthe entire file's processing.\n\nRequired when type is json.","properties":{"resourcePath":{"type":"string","description":"Dot-notation path to the array of records within the JSON structure (e.g.\n\"response.data.customers\"); leave empty when the file's root is already the record\narray. The path must resolve to an array of objects — array indexing and wildcards\nare not supported. A path that resolves to nothing produces zero records without\nraising an error."}}},"xlsx":{"type":"object","description":"Parsing settings for Microsoft Excel (.xlsx) workbooks. Configure when type is \"xlsx\".\nCalculated cell values are extracted rather than formulas; legacy .xls files are not\nsupported — use the modern Open XML format.\n\nRequired when type is xlsx.","properties":{"hasHeaderRow":{"type":"boolean","description":"When true (the default), the first row is read as field names rather than data;\nblank header cells are auto-named and duplicate names are made unique with suffixes.\nWhen false, every row is treated as data and fields receive generic positional names\n(Column1, Column2, ...)."}}},"xml":{"type":"object","description":"Parsing settings for XML documents. Configure when type is \"xml\". resourcePath is an\nXPath expression selecting the elements treated as records; namespaces are handled\nautomatically.\n\nRequired when type is xml.","properties":{"resourcePath":{"type":"string","description":"XPath expression selecting the elements treated as records — each matching element\nbecomes one record, with its child elements as fields. Required for XML parsing;\nthere is no default. Use an absolute path (/Root/Order) when the structure is fixed,\n//Element to match at any depth, or predicates (//Element[@type=\"product\"]) to\nfilter; XPath is case-sensitive, and a non-matching path yields zero records."}}},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"},"fileDefinition":{"type":"object","description":"Parsing via a predefined file definition resource, for formats the standard parsers\ncannot handle — fixed-width files, EDI documents (X12, EDIFACT), and multi-record-type\nor proprietary formats. Configure when type is \"filedefinition\".\n\nRequired when type is filedefinition.","properties":{"_fileDefinitionId":{"type":"string","format":"objectId","description":"File definition resource containing the parsing rules for this format. Must\nreference an existing, accessible file definition — never guess or fabricate the ID.\nDefinitions are reusable across exports, so changing one affects every export that\nuses it."},"allowPartialSuccess":{"type":"boolean","description":"When true, a file whose records partially fail parsing against the file definition\nstill emits the records that did parse, instead of failing the entire file. Applies to\nfile-definition (fixed-width, EDI) parsing only."}}},"filter":{"description":"Selects which files are processed: files matching the filter criteria are exported,\nand non-matching files are skipped entirely before processing begins. Filterable\nfields come from each file's metadata — most providers expose filename, filesize,\nand lastmodified (e.g. [\"endswith\", [\"extract\", \"filename\"], \".csv\"]).","allOf":[{"$ref":"#/components/schemas/Filter"}]},"backupPath":{"type":"string","description":"Path where backup copies of source files are stored before processing."},"directory":{"type":"object","required":["pathMode"],"description":"Structured source location for cloud file-provider sources (Google Drive shared drives,\nBox, Dropbox), replacing the flat path used by classic FTP/S3 sources. Select the location\nby folder ID or by a path relative to a configured storage root via pathMode. For these\ncloud providers, directoryId mode addresses the folder by id alone — the flat\nrelative-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 source directory (e.g. a Google Drive folder ID); set\nwhen pathMode is directoryId. This is the file provider's own ID, not a Celigo resource ID."},"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 (e.g. a Google Drive shared-drive ID) the relative\npath resolves against; set when pathMode is relativePath. The provider's own ID, not a Celigo ID."},"storageRootName":{"type":"string","description":"Display name of the storage root identified by storageRootId, retained for reference in the UI."}}},"backupDirectory":{"type":"object","required":["pathMode"],"description":"Structured destination for backup copies of source files on cloud file providers — the\nfile-provider counterpart to backupPath. Addressed the same way as directory.","properties":{"pathMode":{"type":"string","enum":["relativePath","directoryId"],"description":"How the backup location is addressed — by provider folder ID, or by a path relative to a configured storage root."},"id":{"type":"string","description":"Provider-native folder ID of the backup directory; set when pathMode is directoryId."},"name":{"type":"string","description":"Display name of the backup folder identified by id, kept for readability in the UI;\nthe folder browser fills it when the location is picked visually."},"storageRootId":{"type":"string","description":"Provider-native ID of the storage root the backup relative path resolves against; set when pathMode is relativePath."}}}}},"IncludeParentData":{"type":"boolean","description":"When true and a path to records is configured on the export (`pathToRecords` for MongoDB and DynamoDB, `http.response.resourcePath` for HTTP, `file.json`/`file.xml` `resourcePath` for file-based exports), the data outside that path is attached to each extracted record as a `_PARENT` object, keeping the surrounding envelope available to downstream steps. When omitted or false, records are emitted without `_PARENT`, exactly as before. Constraints: on HTTP exports this option cannot be combined with `useXpathParser: true` (rejected at save with `EXPORT_INCLUDE_PARENT_DATA_XPATH_UNSUPPORTED`), and XML responses must resolve to the default converted-JSON parse mode — a run whose XML parser resolves to DOM/XPath mode (for example, because a connection-level field uses an advanced XPath expression) fails at start with the same error rather than silently omitting `_PARENT`. On file-based exports, it requires `file.type` of `json` with `file.json.resourcePath` configured — XML file envelope capture is not yet supported (the streaming XML parser emits no envelope event; XML support arrives with a parsers-package enhancement) — and any other file configuration is rejected at save with `EXPORT_INCLUDE_PARENT_DATA_UNSUPPORTED_FILE_TYPE`."},"Distributed":{"type":"object","description":"Authentication settings for distributed (real-time listener) exports.","properties":{"bearerToken":{"type":"string","description":"Bearer token used to authenticate inbound distributed export requests. Optional — most\ndistributed listeners (NetSuite, Salesforce real-time) use no token. Write-only and stored\nencrypted; treat it as a sensitive credential.","format":"password"}}},"Cdc":{"type":"object","description":"Change-data-capture configuration for real-time listener exports (the \"Listen for real-time\ndata\" source step). Present only when `type` is `stream`. Set on `MongodbExport` (MongoDB change\nstreams) and `RDBMSExport` (SQL Server CDC and PostgreSQL logical replication); the listener\nstreams change events continuously instead of fetching records on a schedule.\n\nOn save the API normalizes the `properties` grid into the typed fields: a `snapshot.mode`\nentry populates `snapshotMode` and a `capture.mode` entry populates `captureMode`. Every\nstream export must resolve a snapshot mode from one of those two places.","properties":{"captureMode":{"type":"string","enum":["change_streams_update_full","change_streams_update_full_with_pre_image"],"default":"change_streams_update_full","description":"How MongoDB change-stream events are materialized into records. Use\n`change_streams_update_full_with_pre_image` when downstream steps need the document's prior\nstate. MongoDB listeners only — `RDBMSExport` (SQL Server CDC, PostgreSQL) does not set this."},"snapshotMode":{"type":"string","enum":["initial","no_data","when_needed","initial_only"],"default":"no_data","description":"Whether the listener seeds existing rows before streaming changes. Use `initial` to backfill\nexisting data on the first run; `no_data` to stream only new changes."},"slotName":{"type":"string","pattern":"^[a-z_][a-z0-9_]{0,62}$","description":"Name of the PostgreSQL logical replication slot the listener consumes. PostgreSQL listeners\nonly. When omitted, the platform manages the slot name. Must be a valid PostgreSQL\nidentifier (lowercase letters, numbers, or underscores, starting with a letter or\nunderscore)."},"publicationName":{"type":"string","pattern":"^[a-z_][a-z0-9_]{0,62}$","description":"Name of the PostgreSQL publication that defines which tables stream changes. PostgreSQL\nlisteners only. When set, the publication is the sole table filter and `rdbms.tables`\nstays unset. Must be a valid PostgreSQL identifier."},"cursor":{"type":"string","description":"Server-managed stream position marker (where the listener resumes reading change events).\nMaintained by the platform as events are consumed; the UI exposes a reset workflow for it\nunder Cursor Management."},"paths":{"type":"array","items":{"type":"string"},"description":"Dot-notation paths within each raw change event to retain in the emitted record. MongoDB\nlisteners default to `[\"payload\"]` (the whole change document); SQL Server and PostgreSQL\nlisteners commonly select `payload.after`, `payload.op`, and related fields to expose the\nchanged row and operation type."},"intervalTime":{"type":"integer","default":300,"description":"Polling interval, in seconds, at which the listener checks the source for new change events."},"properties":{"type":"array","description":"Connector-engine settings passed through to the underlying CDC connector as name/value\npairs. Mirrors the form's additional-properties grid: the required keys (`capture.mode`\nand `snapshot.mode` for MongoDB, `snapshot.mode` for SQL Server and PostgreSQL) are\nserialized first, followed by optional keys such as `field.exclude.list`,\n`column.include.list`, or `snapshot.collection.filter.overrides.<db>.<collection>`.\nOn save the API also lifts `snapshot.mode` / `capture.mode` entries into the typed\n`snapshotMode` / `captureMode` fields.","items":{"type":"object","required":["name","value"],"properties":{"name":{"type":"string","description":"Connector property key (e.g. `capture.mode`, `snapshot.mode`, `field.exclude.list`)."},"value":{"type":"string","description":"Connector property value."}}}}}},"FileSystem-2":{"type":"object","description":"Defines which files to read from a local or mounted folder on the host running the\non-premise agent. Required when the _connectionId field references a file-system (on-premise)\nconnection; must not be included for other connection types.","required":["directoryPath"],"properties":{"directoryPath":{"type":"string","description":"Folder on the on-premise agent's host to read files from; the agent's OS account must\nhave read permission on it. Accepts a local OS path or a UNC network share, and supports\nhandlebars templates for dynamic folders."}}},"Http-2":{"type":"object","description":"Configuration for HTTP exports. Required when the export's `_connectionId` references a\nconnection of type `http`, and on `NetSuiteHTTPExport` (NetSuite's REST APIs on a `netsuite`\nconnection, where `nsDomainType` supplies the host).","properties":{"type":{"type":"string","enum":["file","blob","csvstream"],"description":"Set to `file` when the export works with files rather than a record-returning endpoint: raw file downloads (PDFs, images, binary data) and the folder mode of the cloud file providers (Google Drive, Box, Dropbox, Azure Blob, Google Cloud Storage, Celigo Storage), where each file in the folder is parsed by the `file` object's `type` (csv, json, xml, xlsx, filedefinition — including EDI) into records; the `file` object must also be configured. Leave undefined for standard data exports that return structured records (JSON, XML, GraphQL, SOAP responses) — the response is then parsed into records for downstream steps. When set to `file` or `blob`, the export appears as a \"Transfer\" step in Flow Builder instead of a standard \"Export\" step."},"formType":{"type":"string","enum":["http","rest","graph_ql","assistant","assistant_graphql"],"description":"Authoring mode of the HTTP request form. Controls which editor the UI presents\n(standard HTTP, legacy REST, GraphQL, or connector-assistant driven) and how the\nrequest fields are interpreted. When omitted, the standard HTTP form is used."},"method":{"type":"string","description":"HTTP method used to request data from the target API. Consult the target API's documentation to determine the appropriate method.","enum":["GET","POST","PUT","PATCH","DELETE"]},"followRedirects":{"type":"boolean","default":true,"description":"When explicitly false, 3xx responses are not followed — the redirect\nresponse itself (status code, `Location` header, body) becomes the\nrecord. Omitted or true follows allowed redirects (the default\nbehavior)."},"maxRedirects":{"type":"integer","minimum":1,"maximum":10,"description":"Caps how many consecutive 3xx redirects are followed. Only applies\nwhen `followRedirects` is not false; to not follow at all, set\n`followRedirects: false` rather than `maxRedirects: 0`. Decimal\nvalues are rejected on save."},"relativeURI":{"type":"string","description":"Resource path appended to the connection's `baseURI` to form the complete request URL. Path segments, query parameters, or the entire value can be built with handlebars expressions for endpoints determined at runtime. For lookup exports (`isLookup: true`) with mappings configured, handlebars always render against the original pre-mapped input record, so fields removed or renamed by mappings remain available for URI construction. On `NetSuiteHTTPExport` the base URL is derived from the NetSuite connection and `nsDomainType`, so the path starts at `/services/rest/...` or `/app/site/hosting/restlet.nl`."},"headers":{"type":"array","description":"Headers specific to this export, merged with (and able to override) headers defined on the connection. Define common headers such as authentication on the connection instead. Values support handlebars expressions; for lookup exports with mappings, values render against the pre-mapped input record.","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the HTTP header to send; a header defined here overrides a same-named header from the connection."},"value":{"type":"string","description":"Value sent for the header. Supports handlebars expressions; for lookup exports with mappings, the value renders against the pre-mapped input record."}}}},"requestMediaType":{"type":"string","description":"Overrides the connection-level request media type for this export. Set only when this endpoint requires a different format than the connection default.","enum":["json","xml","urlencoded","form-data","plaintext"]},"body":{"type":"string","description":"Request body sent with POST, PUT, or PATCH requests, typically carrying query or filter criteria for APIs (such as GraphQL or SOAP) that expect them in the body. The content must match the format set by `requestMediaType` and supports handlebars expressions for dynamic values."},"successMediaType":{"type":"string","description":"Media type used to parse successful response bodies. Set only when the response format differs from the request format.","enum":["json","xml","csv","plaintext"]},"errorMediaType":{"type":"string","description":"Media type used to parse error response bodies. Set only when error responses use a different format than the request.","enum":["json","xml","plaintext"]},"_asyncHelperId":{"type":"string","format":"objectId","description":"AsyncHelper resource that handles polling for long-running operations on APIs that process requests asynchronously (HTTP 202 responses, job tickets, feed or document IDs). Set when the export must submit a request, poll for status, and retrieve results once the external process completes — for example Amazon SP-API feeds or large report generators."},"once":{"type":"object","description":"Callback configuration for once exports, used to mark records as exported in the source system after successful processing.","properties":{"relativeURI":{"type":"string","description":"Relative path (starting with `/`) called on the source system to mark each record as exported after successful processing. Supports handlebars variables and renders against the pre-mapped record — mappings do not apply to the callback URI."},"method":{"type":"string","description":"HTTP method used for the mark-as-exported callback request.","enum":["GET","PUT","POST","PATCH","DELETE"]},"body":{"type":"string","description":"Request body sent with the mark-as-exported callback. Supports handlebars expressions for dynamic values."}}},"paging":{"type":"object","description":"Controls how the export requests subsequent pages when the API returns multi-page responses. The `method` field determines which companion fields are required and how each next page is requested. For the page, skip, and token methods, reference the matching pagination variable (`{{export.http.paging.page}}`, `{{export.http.paging.skip}}`, or `{{export.http.paging.token}}`) in the relative URI or request body — misconfigured pagination is a common cause of incomplete data retrieval.","properties":{"method":{"type":"string","description":"Pagination strategy used to request each subsequent page; match it to the mechanism documented by the target API. Determines which companion fields are required: `token` needs `path` and `pathLocation`, `url` needs `path`, `relativeuri` needs `relativeURI`, `body` needs `body`, while `linkheader` typically needs no extra configuration. Using the wrong method results in errors or incomplete data retrieval.","enum":["linkheader","page","skip","token","url","relativeuri","body"]},"page":{"type":"integer","description":"Starting page number for `method: page`. Set to 1 for APIs whose first page is not zero-indexed; when omitted, paging starts at 0. The value is incremented automatically for each subsequent page request."},"skip":{"type":"integer","description":"Starting offset for `method: skip`. Rarely needed — most APIs start at 0, and the value is incremented by the page size automatically for each subsequent request. Set only when the API requires a non-zero starting offset."},"token":{"type":"string","description":"Initial token for `method: token`. Leave empty for normal pagination — the first request is sent without a token, and subsequent tokens are extracted from each response via `path`. Set only to resume from a known token or for APIs that require a token on the first request."},"path":{"type":"string","description":"Location of the pagination value in each response — the continuation token for `method: token`, or the complete next-page URL for `method: url`. When `pathLocation` is `body`, use a dot-notation JSON path (e.g. `meta.nextToken`); when `header`, use the exact case-sensitive header name. Not used by other pagination methods."},"pathLocation":{"type":"string","description":"Where the export looks for the pagination token referenced by `path`. Required for `method: token`; not used by other pagination methods.","enum":["body","header"]},"pathAfterFirstRequest":{"type":"string","description":"Alternative token location used for responses after the first page, in the same format as `path`. Set only when the API moves the token to a different location in subsequent responses — setting it unnecessarily can cause pagination to fail."},"relativeURI":{"type":"string","description":"Overrides the main relative URI for second and later page requests; leave empty when the main relative URI works for all pages. Build it with handlebars using the `previous_page` context — `previous_page.full_response` (the prior response body), `previous_page.last_record` (the last record of the prior page), and `previous_page.headers` (the prior response headers)."},"body":{"type":"string","description":"Overrides the main request body for second and later page requests, typically for GraphQL or SOAP APIs that paginate through the body; leave empty when the main body works for all pages. Build it with handlebars using the `previous_page` context — `previous_page.full_response`, `previous_page.last_record`, and `previous_page.headers`."},"mergeBodyParamsToPagingBody":{"type":"boolean","description":"Only applies when `paging.method` is `body`. When true, the body\nparameters produced by the export's mappings at runtime are merged\ninto the evaluated `paging.body` for page 2+ requests, so paging\nrequests keep the same mapped parameters as the first page.\nOmitted/false keeps the original behavior — page 2+ requests send\nonly the evaluated `paging.body`."},"linkHeaderRelation":{"type":"string","description":"Link header relation followed for `method: linkheader` when the API uses a value other than the default `next`. Case-sensitive and must exactly match the `rel` value in the Link header, without the `rel=` prefix."},"resourcePath":{"type":"string","description":"Overrides the path to records for second and later page responses. Set only when follow-up pages place records at a different location than the first response; leave empty when all pages share the same structure."},"lastPageStatusCode":{"type":"integer","description":"HTTP status code that signals the last page, replacing the default behavior of treating 404 as the end of pagination. When this status is received, paging stops and the response is not treated as an error. Set only when the API uses a non-404 code (such as 204 or 400) to indicate no more pages."},"lastPagePath":{"type":"string","description":"JSON path to a response-body field that signals the end of pagination, such as a \"has more\" flag or a cursor that empties on the last page. Must be used with `lastPageValues`, which lists the values at this path that stop paging. If the path does not exist in a response, the condition is not considered met."},"lastPageValues":{"type":"array","description":"Values at `lastPagePath` that stop pagination; a match on any entry ends paging. All entries are compared as exact, case-sensitive strings, even for boolean or numeric fields — use `\"true\"`, `\"false\"`, `\"null\"` for JSON null, or `\"\"` for an empty string. Required when `lastPagePath` is set.","items":{"type":"string"}},"maxPagePath":{"type":"string","description":"JSON path to the total page count in the response, used to stop paging once the last page is reached. Only applies to the `page` and `skip` methods. Point it at the total number of pages, not the current page number."},"maxCountPath":{"type":"string","description":"JSON path to the total record count in the response, used to stop paging once all records have been retrieved. Only applies to the `page` and `skip` methods; when both are set, `maxPagePath` takes precedence. Point it at the total number of records, not the count in the current page."}},"if":{"properties":{"method":{"const":"body"}},"required":["method"]},"then":{"required":["body"]},"else":{"if":{"properties":{"method":{"const":"token"}},"required":["method"]},"then":{"required":["path","pathLocation"]},"else":{"if":{"properties":{"method":{"const":"relativeuri"}},"required":["method"]},"then":{"required":["relativeURI"]},"else":{"if":{"properties":{"method":{"const":"url"}},"required":["method"]},"then":{"required":["path"]}}}}},"response":{"type":"object","description":"Controls how records are extracted from the API response and how success or failure is detected at the response level. When the API wraps records in an envelope object, set `resourcePath` to the path of the records array — without it, the entire response body is treated as a single record. Leave this object undefined when the API returns a bare JSON array.","properties":{"resourcePath":{"type":"string","description":"Dot-separated path to the array of records inside the response body (e.g. `data.customers` for `{\"data\": {\"customers\": [...]}}`); without it, a wrapped response is treated as a single record. Values starting with `$` are evaluated as JSONPath instead of dot notation — including unions (`$['orders','invoices'][*]`) and filter expressions (`$.items[?(@.qty>3)]`); all other values keep the existing dot-notation behavior unchanged. Leave undefined when the API returns a bare JSON array. This extracts records from the API response — not to be confused with `oneToMany`/`pathToMany`, which unwrap arrays from input records, or `paging.resourcePath`, which applies only to subsequent page responses."},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"},"resourceIdPath":{"type":"string","description":"Path to the unique identifier within each record, used primarily when processing results of asynchronous import responses. When omitted, the system looks for standard `id` or `_id` fields automatically."},"successPath":{"type":"string","description":"Path to a response field that signals whether the call succeeded, for APIs that return HTTP 200 even on failure. Must be used with `successValues` to define which values at this path count as success."},"successValues":{"type":"array","items":{"type":"string"},"description":"Values at `successPath` that mark the response as successful; any other value is treated as an error. All comparisons are string-based — use `\"true\"` or `\"false\"` for boolean fields."},"errorPath":{"type":"string","description":"Path to the error message in the response body. The value at this path is included in error logs and error records when the API returns an error."},"failPath":{"type":"string","description":"Path to a response field that signals failure even when the HTTP status code is 200 — the inverse of `successPath`. Must be used with `failValues`."},"failValues":{"type":"array","items":{"type":"string"},"description":"Values at `failPath` that mark the response as failed, even when the HTTP status code is 200."},"allowArrayforSuccessPath":{"type":"boolean","description":"When true, treats the value at `successPath` as an array and counts the response as successful if any element matches `successValues`, rather than requiring a single scalar match. Set it for APIs that return per-record status arrays in a batch response."},"twoDArray":{"type":"object","description":"Parsing options for endpoints that return tabular data as a two-dimensional array (rows of cells) rather than an array of objects, such as spreadsheet-style or report APIs.","properties":{"hasHeader":{"type":"boolean","description":"When true, the first row is treated as column headers and used to name the fields of each generated record."},"doNotNormalize":{"type":"boolean","description":"When true, the rows are passed through as raw arrays instead of being normalized into keyed records."}}},"blobFormat":{"type":"string","description":"Controls how the binary response body is decoded for blob exports. Only relevant when `http.type` is `file` or the export type is `blob`.","enum":["utf8","ucs2","utf-16le","ascii","binary","base64","hex"]}}},"_httpConnectorVersionId":{"type":"string","format":"objectId","readOnly":true,"description":"Identifies the HTTP connector version used by this export. Set by the connector framework; client-supplied values are ignored (write-tested)."},"_httpConnectorResourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Identifies the HTTP connector resource used by this export. Set by the connector framework; client-supplied values are ignored (write-tested)."},"sendAuthForFileDownloads":{"type":"boolean","description":"When true, includes authentication headers when downloading files."},"_httpConnectorEndpointId":{"type":"string","format":"objectId","readOnly":true,"description":"Identifies the HTTP connector endpoint configuration used for this export's requests. Set by the connector framework; client-supplied values are ignored (write-tested)."}},"if":{"properties":{"type":{"const":"file"}},"required":["type"],"not":{"properties":{"formType":{"const":"assistant"}},"required":["formType"]}},"then":{"required":["file"]}},"Salesforce-3":{"type":"object","description":"Configuration for Salesforce exports. Required when the export's `_connectionId` references a Salesforce connection; omit for other connection types. The `type` field selects the extraction mode: `soql` runs batch queries and requires the `soql` object, while `distributed` listens for real-time events and requires the `distributed` object. File retrieval (when the export's `type` is `blob`) requires `sObjectType` and `id` instead.","properties":{"type":{"type":"string","description":"Selects the extraction mode and determines which configuration object is required. Use `soql` for scheduled or on-demand batch queries and lookups — it requires the `soql` object and works with both the `rest` and `bulk` APIs and the standard, delta, test, and once export types. Use `distributed` for real-time event handling — it requires the `distributed` object, always appears as a Listener in Flow Builder, ignores the `api` field, and supports only the standard export type.","enum":["soql","distributed"]},"sObjectType":{"type":"string","description":"API name of the Salesforce object the export operates on; the object must exist in the connected org and be accessible to the integration user. Required for distributed exports and for blob exports — blob exports accept only the file storage objects (`Attachment`, `ContentVersion`, `Document`) paired with the `id` field. Optional for SOQL exports, where the object can be inferred from the query."},"id":{"type":"string","description":"Salesforce record ID of the file to retrieve; required for blob exports and not used for `soql` or `distributed` exports. Accepts a static ID or a handlebars expression such as `{{record.Attachment_Id__c}}` to resolve the file at runtime — dynamic IDs require the export to run as a lookup (`isLookup: true`). The ID must belong to the object named in `sObjectType`."},"includeDeletedRecords":{"type":"boolean","description":"When true, SOQL exports use Salesforce's `queryAll()` instead of `query()`, including Recycle Bin records deleted within the past 15 days; each record's `IsDeleted` field identifies deletions. Useful for synchronizing deletes to target systems or maintaining a complete audit trail. Ignored for distributed and blob exports.","default":false},"api":{"type":"string","description":"Salesforce API used to run SOQL queries; ignored for distributed and blob exports. Use `rest` (the default when omitted) for smaller datasets (under 10,000 records) and for lookup exports — the Bulk API is not compatible with `isLookup: true`. Use `bulk` for large data volumes and higher throughput, optionally tuned through the `bulk` object.","enum":["rest","bulk"]},"bulk":{"type":"object","description":"Settings for Salesforce Bulk API 2.0 jobs. Only applies when `api` is `bulk` on a SOQL export; ignored otherwise.","properties":{"maxRecords":{"type":"integer","description":"Caps how many records a single Bulk API job retrieves, which helps prevent timeouts with complex queries or large records. When omitted, Salesforce's default applies. Lower values suit complex or custom objects; higher values improve throughput for simple records.","minimum":10000},"purgeJobAfterExport":{"type":"boolean","default":true,"description":"When true, deletes the Bulk API job in Salesforce after all data is retrieved, keeping the Bulk Data Load Jobs list clean — at the cost of making job details unavailable for later troubleshooting. Has no effect on the data retrieved or the success of the export."}}},"soql":{"type":"object","description":"SOQL query configuration; required when `type` is `soql` and not used otherwise. The query controls which objects, fields, and filter conditions the export retrieves, with either the REST or Bulk API.\n\nRequired when type is soql.","properties":{"query":{"type":"string","description":"SOQL statement passed directly to Salesforce that defines which objects, fields, and records the export retrieves. Supports handlebars — for delta exports, filter with `WHERE LastModifiedDate > {{lastExportDateTime}}` — and relationship queries that fetch parent and child records together. Select only the fields you need and use `ORDER BY` for consistent results across pages.","maxLength":200000}}},"distributed":{"type":"object","description":"Real-time event listener configuration; required when `type` is `distributed` and not used otherwise. The system installs triggers in the connected Salesforce org and delivers records to the flow as create, update, and delete events occur — no scheduling or manual execution is involved.","properties":{"referencedFields":{"type":["array","null"],"description":"Fields from related objects to include in the exported record, written in dot notation (e.g. `Account.Name`, `Owner.Email`, `Custom_Lookup__r.Field__c`). Works with lookup and master-detail relationships, up to 10 unique referenced relationships per export. Referenced fields are retrieved via separate API calls, so include only fields the integration actually needs.","items":{"type":["string","null"]}},"disabled":{"type":"boolean","description":"When true, the listener stops processing events while its configuration and Salesforce triggers stay in place. Events that occur while disabled are ignored, not queued — they are not processed retroactively when re-enabled, so consider a delta export to catch up after extended pauses."},"qualifier":{"type":["string","null"],"description":"Salesforce formula expression that filters which events are processed; when omitted (stored as `null`), all events for the object are processed. Evaluated inside Salesforce before events are sent, which is more efficient than filtering in a later flow step. Reference only fields that exist on the monitored object; formula functions such as `ISCHANGED(Status__c)` are supported."},"batchSize":{"type":["integer","null"],"description":"Controls how many event records are grouped into each real-time processing batch. Stored as `null` when not configured. Smaller batches lower latency for time-sensitive operations; larger batches improve throughput for high-volume objects. Does not limit how many records are processed in total — only how they are grouped.","minimum":4,"maximum":200},"skipExportFieldId":{"type":["string","null"],"description":"API name of a Salesforce checkbox field used to prevent infinite loops in bidirectional syncs: when the integration updates a record, this field is set so the resulting event is ignored, then cleared automatically. Required for bidirectional sync scenarios. The field must be a checkbox dedicated to integration use and updateable by the integration user."},"relatedLists":{"type":["array","null"],"description":"Child record sets to include with the parent record when it changes, one entry per related list. When omitted (stored as `null`), only the primary record is processed. Each related list adds Salesforce API calls and increases payload size.","items":{"type":"object","description":"Defines how to retrieve one type of child record related to the primary object. Configure multiple entries to retrieve different types of children.","properties":{"referencedFields":{"type":"array","description":"API names of the child object fields to retrieve; only the listed fields are included, and an empty array retrieves only the `Id` field. Include only fields the integration needs — each field adds data volume and processing time.","items":{"type":["string","null"]}},"parentField":{"type":"string","description":"API name of the lookup or master-detail field on the child object that references the parent (e.g. `AccountId`, `Parent_Object__c`) — the field's API name, not the relationship name. Used to build the query that fetches children for each parent record."},"sObjectType":{"type":"string","description":"Case-sensitive API name of the child object to retrieve (e.g. `Contact`, `Custom_Child__c`) — use the API name, not the label. The object must have a relationship field to the parent and be accessible to the connected user."},"filter":{"type":"string","description":"SOQL condition that limits which child records are included; when omitted, all related children are retrieved. Provide only the condition expression without the `WHERE` keyword — it is combined automatically with the parent relationship filter."},"orderBy":{"type":"string","description":"Sort order for the retrieved child records, as field names with optional `ASC`/`DESC` directions (e.g. `CreatedDate DESC`). Provide only the fields and directions without the `ORDER BY` keywords. When omitted, Salesforce determines the order."}},"required":["parentField","referencedFields","sObjectType"]}}}}},"if":{"properties":{"type":{"const":"distributed"}},"required":["type"]},"then":{"required":["distributed","sObjectType"]},"else":{"if":{"required":["distributed"]},"then":{},"else":{"if":{"required":["id"]},"then":{"required":["sObjectType"]},"else":{"if":{"not":{"propertyNames":{"enum":["metadata"]}}},"then":{"required":["soql","type"]}}}}},"AS2-2":{"type":"object","description":"Configures AS2 exports and listeners. Optional — the transport settings live on the AS2\nconnection, and an AS2 export is valid with only `file` (parse settings); set this object\nto link a Trading Partner Connector or to transfer raw files as blobs. An AS2 listener\nacts as the flow's source, receiving trading-partner transmissions in near real-time and\nhandling decryption, signature verification, and MDN generation; AS2 (Applicability\nStatement 2) transmits EDI and other data securely over HTTP/S using S/MIME encryption\nand digital signatures.","properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies the partner-specific EDI configuration —\ncommunication protocol, document schemas, mappings, validation rules, and endpoint\ndetails. Set this to link the export to all settings required for AS2 communication\nwith that partner."},"blob":{"type":"boolean","description":"When true, retrieves raw files without parsing them into structured records (rendered as\na \"Transfer\" step in the flow UI). Use only when the file contents are not needed in\nsubsequent steps — for binary files or when parsing is handled downstream. Only\navailable on AS2 and VAN exports."}},"required":[]},"DynamoDB-2":{"type":"object","description":"Defines how records are queried from DynamoDB tables. Required when the _connectionId field\nreferences a DynamoDB connection; must not be included for other connection types. Basic\nexports need region, method, tableName, keyConditionExpression, expressionAttributeNames,\nand expressionAttributeValues; once exports (export type \"once\") additionally need\nonceExportPartitionKey, plus onceExportSortKey for composite-key tables.","required":["region","tableName","keyConditionExpression","expressionAttributeNames","expressionAttributeValues"],"properties":{"region":{"type":"string","enum":["us-east-1","us-east-2","us-west-1","us-west-2","af-south-1","ap-east-1","ap-south-1","ap-northeast-1","ap-northeast-2","ap-northeast-3","ap-southeast-1","ap-southeast-2","ca-central-1","eu-central-1","eu-west-1","eu-west-2","eu-west-3","eu-south-1","eu-north-1","me-south-1","sa-east-1"],"description":"AWS region hosting the DynamoDB table. Must match the region where the table is\ndeployed so the integration can reach it.","default":"us-east-1"},"method":{"type":"string","enum":["query"],"description":"DynamoDB operation used to retrieve items. Only \"query\" is currently supported."},"tableName":{"type":"string","description":"Name of the DynamoDB table to query. Must exactly match an existing table (case-sensitive)."},"keyConditionExpression":{"type":"string","description":"Key condition determining which items the query retrieves. Must include a condition on\nthe partition key and may add sort-key conditions (equality, BETWEEN, begins_with).\nReference attribute names with \"#\" placeholders defined in expressionAttributeNames and\nvalues with \":\" placeholders defined in expressionAttributeValues."},"filterExpression":{"type":"string","description":"Filters query results on non-key attributes after the key condition is applied; omit to\nreturn all items matching the key condition. Uses the same \"#\" and \":\" placeholders\ndefined in expressionAttributeNames and expressionAttributeValues."},"projectionExpression":{"type":"array","items":{"type":"string"},"description":"Attributes to return from each item, reducing data transfer; omit to return all\nattributes. Each array element is one field, referenced via \"#\" placeholders defined in\nexpressionAttributeNames."},"expressionAttributeNames":{"type":"string","description":"JSON string mapping \"#\" placeholders to actual attribute names, e.g.\n{\"#pk\": \"customerId\"}. Placeholders defined here are used in keyConditionExpression,\nfilterExpression, and projectionExpression."},"expressionAttributeValues":{"type":"string","description":"JSON string mapping \":\" placeholders to comparison values, e.g. {\":status\": \"ACTIVE\"}.\nValues can be static or dynamic handlebars expressions such as {{lastExportDateTime}},\nand are referenced from keyConditionExpression and filterExpression."},"onceExportPartitionKey":{"type":"string","description":"Partition key attribute that uniquely identifies items when the export's type is \"once\".\nCeligo uses it to mark items as processed after a successful export, preventing the same\nitems from being exported again on subsequent runs."},"onceExportSortKey":{"type":"string","description":"Sort key attribute used together with onceExportPartitionKey to identify processed items\nwhen the table has a composite primary key. Omit for tables keyed by a partition key\nalone."},"pathToRecords":{"$ref":"#/components/schemas/PathToRecords"},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"}}},"PathToRecords":{"type":"string","maxLength":250,"description":"Path to the array of records inside each document or item fetched from the source; each array element is emitted as an individual record, which keeps records within the 5 MB page-size limit even when the source document exceeds it. Accepts dot notation (e.g. `data.orders`) or JSONPath for values starting with `$`, including unions (`$['orders','invoices'][*]`) and filter expressions (`$.items[?(@.qty>3)]`); a path that resolves to nothing emits zero records for that document or item. When omitted, each document or item is emitted as one record, exactly as before — HTTP and file-based exports configure this same capability through their existing `http.response.resourcePath` and `file.json`/`file.xml` `resourcePath` fields."},"FTP-2":{"type":"object","description":"Defines which files to retrieve from an FTP, FTPS, or SFTP server. Required when the\n_connectionId field references an FTP/SFTP connection; must not be included for other\nconnection types. directoryPath selects the folder, fileNameStartsWith/fileNameEndsWith\nfilter files by name, and backupDirectoryPath controls where files are moved after\nretrieval.","required":["directoryPath"],"properties":{"_tpConnectorId":{"type":"string","format":"objectId","description":"Trading Partner Connector that supplies partner-specific B2B settings for this export.\nWhen set, the export inherits the connector's pre-configured settings; omit to use only\nthe FTP connection details."},"directoryPath":{"type":"string","description":"Directory on the server to retrieve files from, either absolute or relative to the login\ndirectory; the FTP user must have read permission on it. Use forward slashes regardless\nof server OS — paths are case-sensitive on UNIX/Linux servers. Supports handlebars\ntemplates, e.g. archive/{{date 'YYYY-MM-DD'}}."},"fileNameStartsWith":{"type":"string","description":"Only retrieves files whose names start with this value (case-sensitive on most servers);\naccepts static text or handlebars templates. When combined with fileNameEndsWith, files\nmust match both."},"fileNameEndsWith":{"type":"string","description":"Only retrieves files whose names end with this value, commonly a file extension\n(case-sensitive on most servers). When combined with fileNameStartsWith, files must\nmatch both."},"backupDirectoryPath":{"type":"string","description":"Directory on the same server where files are moved after successful export, giving you\nan independent backup; if omitted, files are simply deleted from the source directory\nafter successful export (Celigo also keeps its own copy of processed files for a set\nperiod). Supports static paths or handlebars templates."}}},"JDBC-2":{"type":"object","description":"Configuration object for JDBC (Java Database Connectivity) data integration exports.\n\nThis object is REQUIRED when the _connectionId field references a JDBC database connection\nand must not be included for other connection types. It defines how data is extracted\nfrom relational databases using SQL queries.\n\n**Jdbc export capabilities**\n- Execute custom SQL SELECT statements\n- Support for joins, aggregations, and functions\n- Flexible data retrieval from any accessible tables or views\n- Compatible with all major database systems\n**Critical:** WHAT BELONGS IN THIS OBJECT\n- `query` - **ALWAYS REQUIRED** - The SQL SELECT statement\n- `once` - **REQUIRED** when the export's Object Type is `\"once\"` (set _include_once: true)\n- **DO NOT** put `delta` inside this object - delta is handled via the query\n\n**Delta exports (type: \"delta\")**\nFor delta/incremental exports, do NOT populate a `delta` object inside `jdbc`.\nInstead, use `{{lastExportDateTime}}` or `{{currentExportDateTime}}` directly in the query:\n```json\n{\n  \"type\": \"delta\",\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE updatedAt > {{lastExportDateTime}}\"\n  }\n}\n```\n\n**Once exports (type: \"once\")**\nFor once exports (mark records as processed), populate `jdbc.once.query`:\n```json\n{\n  \"type\": \"once\",\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE exported = false\",\n    \"once\": {\n      \"query\": \"UPDATE customers SET exported = true WHERE id = {{record.id}}\"\n    }\n  }\n}\n```\n\n**Standard exports (type: null or not specified)**\nJust provide the query:\n```json\n{\n  \"jdbc\": {\n    \"query\": \"SELECT * FROM customers WHERE status = 'ACTIVE'\"\n  }\n}\n```","required":["query"],"properties":{"formType":{"type":["string","null"],"enum":["sql","simple",null],"description":"Selects how the export's query is authored. Use `sql` for a hand-written SQL statement in\n`query`; use `simple` to build the query from the `simple` object's table/columns/filter."},"query":{"type":"string","description":"SQL SELECT statement executed to retrieve data, from simple table selections to joins and\naggregations. For delta exports, include {{lastExportDateTime}}/{{currentExportDateTime}}\nin the WHERE clause rather than configuring a separate delta object."},"once":{"type":"object","description":"**CRITICAL: REQUIRED when the export's Object Type is \"once\".**\n\nIf Object Type is \"once\", you MUST set _include_once to true (or include this object).\n\nThis object has ONLY ONE property: \"query\" (a SQL UPDATE string).\nDO NOT create any other properties like \"update\", \"table\", \"set\", \"where\", etc.\n\nCORRECT format:\n```json\n{\"query\": \"UPDATE customers SET exported=true WHERE id={{record.id}}\"}\n```\n\nWRONG format (DO NOT DO THIS):\n```json\n{\"update\": {\"table\": \"customers\", \"set\": {...}}}\n```\n","properties":{"query":{"type":"string","description":"**REQUIRED** - A SQL UPDATE statement string to mark records as processed.\n\nThis is a plain SQL UPDATE query string, NOT a structured object.\n\nThe query runs AFTER each record is successfully exported, setting a flag\nto indicate the record has been processed.\n\nFormat: \"UPDATE <table> SET <column>=<value> WHERE <id_column>={{record.<id_field>}}\"\n\nExample: \"UPDATE customers SET exported=true WHERE id={{record.id}}\"\n\nThe {{record.id}} placeholder is replaced with the actual record ID from each exported row.\n"}}},"simple":{"type":"object","description":"Visual query-builder configuration used when `formType` is `simple`; the platform builds\nthe SELECT from these instead of raw SQL. Only relevant when `formType` is `simple`.","properties":{"table":{"type":"string","description":"Table or view to select rows from."},"columns":{"type":"array","items":{"type":"string"},"description":"Columns to return; an empty list selects all columns."},"filter":{"description":"Filter applied as the WHERE clause for the generated query, stored as the\nplatform's structured filter-rules object.","allOf":[{"$ref":"#/components/schemas/Filter"}]}}}}},"MongoDB-2":{"type":"object","description":"Defines how documents are retrieved from MongoDB collections. Required when the\n_connectionId field references a MongoDB connection; must not be included for other\nconnection types. Supports find queries with optional filter criteria and field projections.","required":["collection"],"properties":{"method":{"type":"string","enum":["find"],"description":"MongoDB operation used to retrieve documents. Only \"find\" is currently supported,\nequivalent to db.collection.find(filter, projection)."},"collection":{"type":"string","description":"Name of the MongoDB collection to query. Case-sensitive and must reference an existing\ncollection in the connected database."},"filter":{"type":"string","description":"MongoDB query document, as a JSON string, that selects which documents to export; omit\nto return every document in the collection. Supports standard MongoDB query operators\nand handlebars variables for dynamic values — for example\n{\"lastModified\": {\"$gt\": \"{{lastRun}}\"}} for incremental processing."},"projection":{"type":"string","description":"MongoDB projection document, as a JSON string, that limits which fields are returned;\nomit to return all fields. Use 1 to include fields or 0 to exclude them — the two modes\ncannot be mixed except for _id, which is included by default unless explicitly excluded.\nProjection affects only the shape of returned documents, not which documents match."},"pipeline":{"type":"string","description":"MongoDB aggregation pipeline, as a JSON-array string of stages, used instead of a plain\nfind when documents need to be transformed, joined, or grouped server-side (e.g. `$match`,\n`$lookup`, `$group`). When set, it takes the place of `filter`/`projection`. Supports\nhandlebars variables for incremental processing."},"readPreference":{"type":"string","enum":["doNotOverride","primary","primaryPreferred","secondary","secondaryPreferred","nearest"],"default":"doNotOverride","description":"Overrides which replica-set member this export reads from, trading consistency for load\ndistribution. Leave at `doNotOverride` to inherit the connection's setting."},"pathToRecords":{"$ref":"#/components/schemas/PathToRecords"},"includeParentData":{"$ref":"#/components/schemas/IncludeParentData"}}},"NetSuite-3":{"type":"object","description":"NetSuite-specific export configuration. Required when `_connectionId` references a NetSuite\nconnection; omit for all other connection types. Supports saved-search, RESTlet, and\ndistributed (SuiteApp) exports, plus two file cabinet modes: blob exports transfer files\nas-is (set the export's top-level `type` to `blob` and configure `netsuite.blob`), while\nfile exports parse file contents into records (leave the export's top-level `type` unset\nand configure `netsuite.file`).","properties":{"type":{"type":"string","enum":["search","basicSearch","metadata","selectoption","restlet","getList","getServerTime","distributed","file"],"description":"Controls how data is retrieved from NetSuite and which sibling object must be configured:\n`search` pairs with `netsuite.searches`, `restlet` with `netsuite.restlet`, `distributed`\nwith `netsuite.distributed`, and `file` with `netsuite.file.folderInternalId`. For blob\nexports (raw file transfer without parsing), leave this field unset, set the export's\ntop-level `type` to `blob`, and configure `netsuite.internalId` instead. For lookups\n(`isLookup: true`), prefer `restlet`, which supports `suiteapp2.0` saved searches with\ndynamic inputs; `search` is limited for dynamic lookups."},"searches":{"type":"array","description":"Saved search configurations that query NetSuite for records. Each entry references a\nsaved search by internal ID, names the record type being searched, and can add filter\ncriteria.","items":{"type":"object","properties":{"savedSearchId":{"type":"string","description":"Internal ID of the NetSuite saved search to run."},"recordType":{"type":"string","description":"NetSuite record type being searched, as its exact lowercase script ID\n(e.g. \"customer\", \"salesorder\") — not the display name."},"criteria":{"type":"array","description":"Additional filter criteria applied on top of the saved search.","items":{"type":"object","properties":{"field":{"type":"string","description":"Script-id of the search column to filter on.  REQUIRED.\nMust come from the record type's SEARCH COLUMNS list\n(NOT the body-fields list).  Becomes the `name`\nargument of NetSuite's `search.createFilter()`."},"operator":{"type":"string","description":"Comparison operator.  REQUIRED.  Supported values\ndepend on the column's data type (e.g. `is`,\n`isnot`, `after`, `before`, `anyof`,\n`noneof`, `contains`, `startswith`,\n`greaterthan`, `lessthan`).  Must be a string\nNetSuite recognises -- mismatched operators cause\nruntime rejection."},"join":{"type":"string","description":"Optional join name when filtering through a related\nrecord (e.g. `customer` from a sales order).  Use\nonly when the column lives on a related record and\nNetSuite supports the join from the base record type."},"searchValue":{"description":"Value (or array of values for `anyof`-style\noperators) to compare against the column.  Required\nfor most operators; omit for the few unary operators\n(e.g. `isempty`, `isnotempty`)."}},"required":["field","operator"]}}}}},"metadata":{"type":"object","description":"Supplemental metadata associated with the NetSuite export."},"selectoption":{"type":"object","description":"Configuration for `selectoption` exports, which retrieve the available select options\nfor a NetSuite field."},"customFieldMetadata":{"type":"object","description":"Metadata describing the custom fields defined in the NetSuite account."},"skipGrouping":{"type":"boolean","description":"When true, each result row is processed individually instead of being aggregated with\nrelated rows. When false or omitted, related rows are grouped before processing."},"statsOnly":{"type":"boolean","description":"When true, returns only summary statistics about matching records instead of the\ndetailed records themselves."},"internalId":{"type":"string","description":"Internal ID of the file in the NetSuite file cabinet to export. Required for blob\nexports (the export's top-level `type` is `blob`); the file is transferred as-is\nwithout parsing. For parsed file exports, use `netsuite.file.folderInternalId` instead."},"blob":{"type":"object","properties":{"purgeFileAfterExport":{"type":"boolean","description":"When true, permanently deletes the file from the NetSuite file cabinet after a\nsuccessful export. When false or omitted, the file is left in place."}},"description":"Configuration for blob exports, which transfer files from the NetSuite file cabinet\nas-is without parsing them into records. To export a blob, set the export's top-level\n`type` to `blob`, set `netsuite.internalId` to the file's internal ID, and leave\n`netsuite.type` unset. Use `netsuite.file` instead when file contents should be parsed\ninto records."},"restlet":{"type":"object","properties":{"recordType":{"type":"string","description":"NetSuite record type the RESTlet operates on, as its script ID. Custom record\ntypes use the \"customrecord_\" prefix."},"batchSize":{"type":"number","description":"Number of records the RESTlet returns per request (default 1000). This, not the\nexport's `pageSize`, sizes each NetSuite call; the export keeps requesting until the\nsearch is exhausted. Larger batches reduce round-trips but raise NetSuite governance\nand memory use per call; tune to the record's size. In a test run or preview the\nrequest is capped at the export's `pageSize` (default 20) instead."},"searchId":{"type":["string","null"],"description":"Internal ID of the NetSuite saved search the RESTlet executes. Results follow the\nsaved search's current definition, so editing the search in NetSuite changes the\nexport output without changing this ID."},"useSS2Restlets":{"type":"boolean","description":"When true, calls SuiteScript 2.0 RESTlets. When false or omitted, legacy\nSuiteScript 1.0 RESTlets are used.\nSet at step creation together with restletVersion — the same\ncreation-time-only constraint applies (see restletVersion)."},"restletVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"RESTlet version the export invokes. Defaults to `suiteapp2.0` when\n`useSS2Restlets` is true, `suitebundle` otherwise.\nThe version is fixed when the step is created — the Advanced selector is\ndisabled on existing steps, and migrating an existing export to a\ndifferent version means recreating or cloning it."},"criteria":{"type":"array","description":"Filter conditions added on top of the saved search (the UI's \"Additional search\ncriteria\"). Each condition pairs a search column with an operator and one or two\ncomparison values, and the conditions are combined with AND — every one must match.","items":{"type":"object","properties":{"field":{"type":"string","description":"Script-id of the search column to filter on. This is the primary\nfield the criterion is evaluated against (e.g. \"trandate\", \"status\")."},"join":{"type":"string","description":"Join relationship that applies this filter to a related record's fields instead\nof the base record. Must match a join name NetSuite defines for the record type."},"operator":{"type":"string","enum":["after","allof","any","anyof","before","between","contains","doesnotcontain","doesnotstartwith","equalto","greaterthan","greaterthanorequalto","haskeywords","is","isempty","isnot","isnotempty","lessthan","lessthanorequalto","noneof","notafter","notallof","notbefore","notbetween","notequalto","notgreaterthan","notlessthan","notlessthanorequalto","noton","notonorafter","notonorbefore","notwithin","on","onorafter","onorbefore","startswith","within"],"description":"Comparison operator applied between the column and the search value, using\nNetSuite's lowercase search operator names. Which operators a column accepts\ndepends on its data type."},"searchValue":{"description":"Value the column is compared against — a string, a number, or an array of\nvalues for `anyof`-style operators. On lookup steps it may be a Handlebars\nexpression rendered from the incoming record (for example `{{poNumber}}`)."},"searchValue2":{"description":"Second comparison value for the two-value operators `between` and `notbetween`\n(the upper bound). Omit for every other operator."},"formula":{"type":"string","description":"NetSuite formula expression used as the filter condition, for criteria that\nstandard field-operator-value comparisons can't express."}}}},"columns":{"type":"array","description":"Search columns the RESTlet returns for each matching record. With a saved search,\nomit this to return the search's own result columns; an ad-hoc search (no\n`searchId`) must list its columns here.","items":{"type":"object","properties":{"_id":{"type":"string","readOnly":true,"description":"Subdocument id the server assigns to the column entry on save. Not a NetSuite\nfield id — never set it when authoring."},"name":{"type":"string","description":"Column field name as referenced in the search."},"join":{"type":"string","description":"Join relationship that sources this column from a related record instead of\nthe base record. Supports dot notation for nested joins (e.g. \"employee.manager\")."},"summary":{"type":"string","enum":["group","sum","count","avg","min","max"],"description":"Summary type applied to this column, turning the search into a summary search."},"formula":{"type":"string","description":"NetSuite formula expression that computes this column's value at runtime, such\nas a CASE statement or date formatting, instead of reading a stored field."},"label":{"type":"string","description":"Display name for the column; it becomes the property name of the column's value\nin each exported record."},"sort":{"type":"boolean","description":"When true, query results are sorted by this column's values."}}}},"markExportedBatchSize":{"type":"integer","description":"Number of records updated per RESTlet call when marking records as exported in\nNetSuite (live: an integer count, e.g. 100).","maximum":100,"minimum":1},"hooks":{"type":["object","null"],"properties":{"batchSize":{"type":"number","description":"Legacy location of the RESTlet page size; `restlet.batchSize` is the current\nfield and takes precedence when both are set."},"preSend":{"type":"object","properties":{"fileInternalId":{"type":"string","description":"Internal ID of the JavaScript file in the NetSuite File Cabinet that defines\nthe hook function. Must be readable by the role behind the export's connection."},"function":{"type":"string","description":"Name of the global function in that file that NetSuite invokes as the pre\nsend hook."},"configuration":{"type":"object","description":"Free-form JSON stored with the hook reference for the script's own use. Custom\nsettings in scope for the export are passed to the function separately as\n`options.settings`."}},"description":"SuiteScript hook that runs inside NetSuite after a page of records is collected\nand before it is sent to the Celigo platform."}},"description":"SuiteScript hooks for the export. Only `preSend` exists on exports; imports carry\nthe pre map, post map, and post submit hooks."},"cLocked":{"type":"object","description":"Lock state that prevents modification of the configuration when set."}},"description":"Configuration for `restlet` exports. Identifies the RESTlet script that retrieves data\nfrom NetSuite, along with the saved search, criteria, and columns it executes with."},"distributed":{"type":"object","properties":{"recordType":{"type":"string","description":"NetSuite record type the distributed export listens to, as its exact lowercase\nscript ID (e.g. \"customer\", \"salesorder\") — not the display name. Custom record\ntypes use the \"customrecord_\" prefix."},"executionContext":{"type":"array","description":"NetSuite execution contexts that trigger this distributed export. A record change\nfires the export only when it occurs in one of the listed contexts.","default":["userinterface","webstore"],"items":{"type":"string","enum":["userinterface","webservices","csvimport","offlineclient","portlet","scheduled","suitelet","custommassupdate","workflow","webstore","userevent","mapreduce","restlet","webapplication","restwebservices"]}},"disabled":{"type":"boolean","description":"When true, disables the distributed export so record changes no longer trigger it.\nWhen false or omitted, the export remains active."},"executionType":{"type":"array","description":"Record operations that trigger this distributed export. A record event fires the\nexport only when its operation matches one of the listed types.","default":["create","edit","xedit"],"items":{"type":"string","enum":["create","edit","delete","xedit","copy","view","cancel","approve","reject","pack","ship","markcomplete","reassign","editforecast","dropship","specialorder","orderitems","paybills","print","email"]}},"qualifier":{"type":["array","string","null"],"description":"Qualification criteria that further restrict which record changes the distributed\nexport processes. Stored either as a filter-expression array (current format), a\nlegacy expression string, or null when no qualifier is set."},"skipExportFieldId":{"type":"string","description":"ID of the NetSuite field used to flag records that should be skipped during export.\nOnly affects export output; the data remains unchanged in NetSuite."},"hooks":{"type":"object","properties":{"preSend":{"type":"object","properties":{"fileInternalId":{"type":"string","description":"Internal ID of the file in the NetSuite file cabinet that the preSend hook references."},"function":{"type":"string","description":"Name of the function invoked as the preSend hook. The function can validate\nor modify the payload before it is sent."},"configuration":{"type":"object","description":"Settings that control the preSend hook's behavior."}},"description":"Hook invoked immediately before the payload is sent, allowing custom\nprocessing, modification, or validation."}},"description":"Custom hook functions executed at defined points in the distributed export lifecycle."},"sublists":{"type":["array","null"],"items":{"type":"string"},"description":"IDs of the NetSuite sublists to include with each exported record (for\nexample `item` for transaction line items). Records are exported with\nbody fields only when no sublists are selected. Legacy documents may\nstore null (equivalent to no sublists)."},"referencedFields":{"type":"object","description":"Field identifiers referenced by the distributed export."},"relatedLists":{"type":"object","description":"Related record lists associated with the exported NetSuite record."},"forceReload":{"type":"boolean","description":"When true, bypasses cached data and reloads directly from the source."},"ioEnvironment":{"type":"string","description":"integrator.io environment the distributed NetSuite bundle communicates with. Live-observed values include `production` and `staging`."},"ioDomain":{"type":"string","description":"integrator.io domain the distributed NetSuite bundle calls back to (e.g.\n`integrator.io`). Live-observed on distributed exports."},"lastSyncedDate":{"type":"string","format":"date-time","description":"Timestamp when the distributed export last completed a successful synchronization.\nNot set until the first successful sync."},"settings":{"type":"object","description":"Additional key-value configuration settings for the distributed export."},"useSS2Framework":{"type":"boolean","description":"When true, the distributed export runs on the SuiteScript 2.0 framework. When\nfalse or omitted, the legacy SuiteScript 1.0 framework is used.\nSet at step creation together with frameworkVersion — the same\ncreation-time-only constraint applies (see frameworkVersion)."},"frameworkVersion":{"type":"string","enum":["suitebundle","suiteapp1.0","suiteapp2.0"],"description":"SuiteApp framework version used by the distributed export.\nFixed when the step is created — the Advanced selector is disabled on\nexisting steps, and migrating an existing listener to a different\nversion means recreating or cloning it."}},"description":"Configuration for `distributed` exports, which use the NetSuite SuiteApp to fire\nreal-time, event-driven exports when records change. Define the record type to listen\nto and the execution contexts and operations that trigger it."},"getList":{"type":"array","description":"Configuration for `getList` exports — a list of record references, each retrieving one\nNetSuite record by its identifier. The export returns the fetched records in list order.","items":{"type":"object","properties":{"type":{"type":"string","description":"Standard NetSuite record type to retrieve (e.g. \"customer\", \"salesOrder\").\nUsed when `typeId` is absent. Case-sensitive."},"typeId":{"type":"string","description":"Script ID of a custom record type (\"customrecord_...\") or custom transaction\ntype (\"customtransaction_...\"). Takes precedence over `type` when present.\nCase-sensitive."},"internalId":{"type":"string","description":"Internal ID of the NetSuite record to retrieve. Assigned by NetSuite at record\ncreation and unique within the record type. Takes precedence over `externalId`."},"externalId":{"type":"string","description":"Identifier assigned by an external system, used to reference and synchronize the\nrecord across systems. Distinct from the NetSuite-assigned internal ID."}}}},"searchPreferences":{"type":"object","properties":{"bodyFieldsOnly":{"type":"boolean","description":"When true, search results include only the record's body fields and exclude fields\nfrom joined or related records. Reduces payload size when related data isn't needed."},"pageSize":{"type":"number","description":"Number of search results NetSuite returns per page."},"returnSearchColumns":{"type":"boolean","description":"When true, search results include the column data defined in the search. When\nfalse, column data is omitted and results contain only minimal record information."}},"description":"Preferences that control how NetSuite executes searches and shapes the results it returns."},"file":{"type":"object","description":"Configuration for file exports, which retrieve files from the NetSuite file cabinet\nand parse their contents (CSV, XML, JSON) into records. Leave the export's top-level\n`type` unset when using this object; for raw transfers without parsing, use\n`netsuite.blob` with the export's `type` set to `blob` instead.","properties":{"folderInternalId":{"type":"string","description":"Internal ID of the NetSuite file cabinet folder to export files from. Accepts a\nhandlebars expression (e.g. `{{record.folderId}}`) when the folder must be\nselected dynamically from record data. The ID is stable across folder renames and\nmoves, but may differ between non-production and production accounts."},"backupFolderInternalId":{"type":"string","description":"Internal ID of the NetSuite file cabinet folder where backup files are stored.\nIDs are account-specific and do not transfer between non-production and production\nenvironments."},"fileNameStartsWith":{"type":"string","description":"Optional prefix filter. Only files whose names start with this string are\nexported from the configured folder."},"fileNameEndsWith":{"type":"string","description":"Optional suffix filter. Only files whose names end with this string (e.g. a\n\".csv\" extension) are exported from the configured folder."}}}},"required":[]},"RDBMS-2":{"type":"object","description":"Configuration object for Relational Database Management System (RDBMS) data integration exports.\n\nThis object defines how data is read from a relational database and must not be included\nfor other connection types. For query-type exports (standard, delta, once) it is REQUIRED\nwhen the _connectionId field references an RDBMS database connection and holds the SQL\nquery. For real-time stream listeners (`type: \"stream\"`) it holds the CDC watch list\n(`tables`) instead — and a PostgreSQL listener scoped by a publication\n(`cdc.publicationName`) may omit this object entirely.\n\n**Rdbms export capabilities**\n- Execute custom SQL SELECT statements\n- Support for joins, aggregations, and functions\n- Flexible data retrieval from any accessible tables or views\n- Compatible with all major database systems\n\n**Critical:** WHAT BELONGS IN THIS OBJECT\n- `query` - The SQL SELECT statement - required for every query-type export (standard, delta, once)\n- `once` - **REQUIRED** when the export's Object Type is `\"once\"` (set _include_once: true)\n- `tables` - the CDC listener watch list - real-time stream listeners (`type: \"stream\"`) only\n- **DO NOT** put `delta` inside this object - delta is handled via the query\n- **DO NOT** author `query` on a stream listener - real-time CDC exports have no SQL query\n\n**Delta exports (type: \"delta\")**\nFor delta/incremental exports, do NOT populate a `delta` object inside `rdbms`.\nInstead, use `{{lastExportDateTime}}` or `{{currentExportDateTime}}` directly in the query:\n```json\n{\n  \"type\": \"delta\",\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE updatedAt > {{lastExportDateTime}}\"\n  }\n}\n```\n\n**Once exports (type: \"once\")**\nFor once exports (mark records as processed), populate `rdbms.once.query`:\n```json\n{\n  \"type\": \"once\",\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE exported = false\",\n    \"once\": {\n      \"query\": \"UPDATE customers SET exported = true WHERE id = {{record.id}}\"\n    }\n  }\n}\n```\n\n**Standard exports (type: null or not specified)**\nJust provide the query:\n```json\n{\n  \"rdbms\": {\n    \"query\": \"SELECT * FROM customers WHERE status = 'ACTIVE'\"\n  }\n}\n```\n\n**Real-time CDC listener exports (type: \"stream\")**\nSQL Server and PostgreSQL connections support change-data-capture listeners that stream row\nchanges continuously (the `cdc` object holds the listener settings). A stream export has NO\n`query` — it watches `tables`:\n```json\n{\n  \"type\": \"stream\",\n  \"rdbms\": { \"tables\": \"public.orders,public.customers\" },\n  \"cdc\": { \"properties\": [{ \"name\": \"snapshot.mode\", \"value\": \"no_data\" }] }\n}\n```\nA PostgreSQL listener can instead scope tables through a publication\n(`cdc.publicationName`), in which case `tables` stays unset and the `rdbms` object may be\nomitted entirely.","properties":{"tables":{"type":"string","description":"Comma-separated list of tables the CDC listener watches for changes. Used only by real-time\nlistener exports (`type` is `stream`); standard SQL exports use `query` instead. SQL Server\nlisteners use fully-qualified three-part names (`database.schema.table`); PostgreSQL\nlisteners use two-part names (`schema.table`)."},"query":{"type":"string","description":"SQL SELECT statement executed to retrieve data, from simple table selections to joins,\naggregations, and parameterized queries with Handlebars expressions. Query-type exports\nonly — real-time stream listeners (`type` is `stream`) have no SQL query. For delta exports,\nreference {{lastExportDateTime}}/{{currentExportDateTime}} directly in the query rather\nthan configuring a separate delta object."},"once":{"type":"object","description":"**CRITICAL: REQUIRED when the export's Object Type is \"once\".**\n\nIf Object Type is \"once\", you MUST set _include_once to true (or include this object).\n\nThis object has ONLY ONE property: \"query\" (a SQL UPDATE string).\nDO NOT create any other properties like \"update\", \"table\", \"set\", \"where\", etc.\n\nCORRECT format:\n```json\n{\"query\": \"UPDATE customers SET exported=true WHERE id={{record.id}}\"}\n```\n\nWRONG format (DO NOT DO THIS):\n```json\n{\"update\": {\"table\": \"customers\", \"set\": {...}}}\n```\n","properties":{"query":{"type":"string","description":"**REQUIRED** - A SQL UPDATE statement string to mark records as processed.\n\nThis is a plain SQL UPDATE query string, NOT a structured object.\n\nThe query runs AFTER each record is successfully exported, setting a flag\nto indicate the record has been processed.\n\nFormat: \"UPDATE <table> SET <column>=<value> WHERE <id_column>={{record.<id_field>}}\"\n\nExample: \"UPDATE customers SET exported=true WHERE id={{record.id}}\"\n\nThe {{record.id}} placeholder is replaced with the actual record ID from each exported row.\n"}}}}},"S3-3":{"type":"object","description":"Defines which files to retrieve from an Amazon S3 bucket. Required when the _connectionId\nfield references an AWS S3 connection; must not be included for other connection types.\nregion and bucket locate the source, keyStartsWith/keyEndsWith filter objects by key, and\nbackupBucket with keyPrefix controls where files are moved after retrieval.","required":["region","bucket"],"properties":{"region":{"type":"string","default":"us-east-1","description":"AWS region where the bucket is located. Case-insensitive; the value is normalized to\nlowercase."},"bucket":{"type":"string","description":"S3 bucket to retrieve files from. The connection's AWS credentials must have\ns3:ListBucket and s3:GetObject permissions on it."},"keyStartsWith":{"type":"string","description":"Only retrieves objects whose keys start with this value (case-sensitive), effectively\nselecting a folder in S3's flat key structure. When combined with keyEndsWith, objects\nmust match both."},"keyEndsWith":{"type":"string","description":"Only retrieves objects whose keys end with this value (case-sensitive), commonly a file\nextension. When combined with keyStartsWith, objects must match both."},"backupBucket":{"type":"string","description":"Bucket in the same region where files are moved after successful export, giving you an\nindependent backup; if omitted, files are simply deleted from the source bucket after\nsuccessful export (Celigo also keeps its own copy of processed files for a set period).\nThe connection's AWS credentials must have s3:PutObject permission on this bucket."},"keyPrefix":{"type":"string","description":"Prefix prepended to each file's name when it is moved to the backup bucket; accepts\nstatic text or handlebars templates. The original directory structure is not preserved —\nonly the filename is appended to this prefix. Ignored unless backupBucket is set."}}},"Wrapper-3":{"type":"object","description":"Configuration for Wrapper exports, which delegate data retrieval to custom connector code\n(typically a stack-hosted function) rather than a built-in adaptor. Required when the\n_connectionId field references a wrapper connection.","required":["function"],"properties":{"function":{"type":"string","description":"Name of the function the wrapper invokes to retrieve records. Must match a callable\nfunction in the wrapper's execution context; names are case-sensitive."},"configuration":{"oneOf":[{"type":"object","additionalProperties":true},{"type":"array","maxItems":0}],"description":"Free-form settings passed to the wrapper function at runtime (connector-specific keys such\nas method, apiVersion, headers, relativePath, or body). Structure is defined by the wrapper\ncode, not by this schema. May be stored as an empty array when no settings exist."}}},"Parsers":{"type":"array","description":"Configuration for parsing XML payloads (i.e. files, HTTP responses, etc.). Use this field when you need to process XML data\nand transform it into a JSON records.\n\n**Implementation notes**\n\n- This is where you configure how to parse XML data in your resource\n- Although defined as an array, you typically only need a single parser configuration\n- Currently only XML parsing is supported\n- Only configure this field when working with XML data that needs structured parsing\n","items":{"type":"object","properties":{"version":{"type":"string","description":"Version identifier for the parser configuration format. Currently only version \"1\" is supported.\n\nAlways set this field to \"1\" as it's the only supported version at this time.\n","enum":["1"]},"type":{"type":"string","description":"Defines the type of parser to use. Currently only \"xml\" is supported.\n\nWhile the system is designed to potentially support multiple parser types in the future,\nat this time only XML parsing is implemented, so this field must be set to \"xml\".\n","enum":["xml"]},"name":{"type":"string","description":"Optional identifier for the parser configuration. This field is primarily for documentation\npurposes and is not functionally used by the system.\n\nThis field can be omitted in most cases as it's not required for parser functionality.\n"},"rules":{"type":"object","description":"Configuration rules that determine how XML data is parsed and converted to JSON.\nThese settings control the structure and format of the resulting JSON records.\n\n**Parsing options**\n\nThere are two main parsing strategies available:\n- **Automatic parsing**: Simple but produces more complex output\n- **Custom parsing**: More control over the resulting JSON structure\n","properties":{"V0_json":{"type":"boolean","description":"Controls the XML parsing strategy.\n\n- When set to **true** (Automatic): XML data is automatically converted to JSON without\n  additional configuration. This is simpler to set up but typically produces more complex\n  and deeply nested JSON that may be harder to work with.\n\n- When set to **false** (Custom): Gives you more control over how the XML is converted to JSON.\n  This requires additional configuration (like listNodes) but produces cleaner, more\n  predictable JSON output.\n\nMost implementations use the Custom approach (false) for better control over the output format.\n"},"listNodes":{"type":"array","description":"Specifies which XML nodes should be treated as arrays (lists) in the output JSON.\n\nIt's not always possible to automatically determine if an XML node should be a single value\nor an array. Use this field to explicitly identify nodes that should be treated as arrays,\neven if they appear only once in the XML.\n\nEach entry should be a simplified XPath expression pointing to the node that should be\ntreated as an array.\n\nOnly relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"includeNodes":{"type":"array","description":"Limits which XML nodes are included in the output JSON.\n\nFor large XML documents, you can use this field to extract only the nodes you need,\nreducing the size and complexity of the resulting JSON. Only nodes specified here\n(and their children) will be included in the output.\n\nEach entry should be a simplified XPath expression pointing to nodes to include.\n\nOnly relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"excludeNodes":{"type":"array","description":"Specifies which XML nodes should be excluded from the output JSON.\n\nSometimes it's easier to specify which nodes to exclude rather than which to include.\nUse this field to identify nodes that should be omitted from the output JSON.\n\nEach entry should be a simplified XPath expression pointing to nodes to exclude.\n\nOnly relevant when V0_json is set to false (Custom parsing).\n","items":{"type":"string"}},"stripNewLineChars":{"type":"boolean","description":"Controls whether newline characters are removed from text values.\n\nWhen set to true, all newline characters (\\n, \\r, etc.) will be removed from\ntext content in the XML before conversion to JSON.\n","default":false},"trimSpaces":{"type":"boolean","description":"Controls whether leading and trailing whitespace is trimmed from text values.\n\nWhen set to true, all values will have leading and trailing whitespace removed\nbefore conversion to JSON.\n","default":false},"attributePrefix":{"type":"string","description":"Specifies a character sequence to prepend to XML attribute names when converted to JSON properties.\n\nIn XML, both elements and attributes can exist at the same level, but in JSON this distinction is lost.\nTo maintain the distinction between element data and attribute data in the resulting JSON, this prefix\nis added to attribute names during conversion.\n\nFor example, with attributePrefix set to \"Att-\" and an XML element like:\n```xml\n<product id=\"123\">Laptop</product>\n```\n\nThe resulting JSON would be:\n```json\n{\n  \"product\": \"Laptop\",\n  \"Att-id\": \"123\"\n}\n```\n\nThis helps maintain the distinction between element content and attribute values in the\nconverted JSON, making it easier to reference specific data in downstream processing steps.\n"},"textNodeName":{"type":"string","description":"Specifies the property name to use for element text content when an element has both\ntext content and child elements or attributes.\n\nWhen an XML element contains both text content and other nested elements or attributes,\nthis field determines what property name will hold the text content in the resulting JSON.\n\nFor example, with textNodeName set to \"value\" and an XML element like:\n```xml\n<item id=\"123\">\n  Laptop\n  <category>Electronics</category>\n</item>\n```\n\nThe resulting JSON would be:\n```json\n{\n  \"item\": {\n    \"value\": \"Laptop\",\n    \"category\": \"Electronics\",\n    \"id\": \"123\"\n  }\n}\n```\n\nThis allows for unambiguous parsing of complex XML structures that mix text content with\nchild elements. Choose a name that's unlikely to conflict with actual element names in your XML.\n"}}}}}},"MockOutput":{"type":["object","null"],"description":"Sample data that simulates the output from an export for testing and configuration purposes.\n\nMock output allows you to configure and test flows without executing the actual export or\nwaiting for real-time data to arrive. This is particularly useful for:\n- Initial flow configuration and testing\n- Mapping development without requiring live data\n- Generating metadata for downstream flow steps\n- Creating realistic test scenarios\n- Documenting expected data structures\n\n**Structure**\n\nThe mock output must follow the integrator.io canonical format, which consists of a\n`page_of_records` array containing record objects. Each record object has a `record`\nproperty that contains the actual data fields.\n\n```json\n{\n  \"page_of_records\": [\n    {\n      \"record\": {\n        \"field1\": \"value1\",\n        \"field2\": \"value2\",\n        ...\n      }\n    },\n    ...\n  ]\n}\n```\n\n**Usage**\n\nWhen executing a test run or configuring a flow, integrator.io will use this mock output\ninstead of executing the export to retrieve live data. This allows you to:\n- Test mappings with representative data\n- Configure downstream flow steps without waiting for real data\n- Simulate various data scenarios\n\n**Limitations**\n\n- Maximum of 10 records\n- Maximum size of 1 MB\n- Must follow the canonical format shown above\n\nMock output can be populated automatically from preview data or entered manually.\n","properties":{"page_of_records":{"type":"array","description":"Array of record objects in the integrator.io canonical format.\n\nEach item in this array represents one record that would be processed\nby the flow during execution.\n","items":{"type":"object","properties":{"record":{"type":"object","description":"Container for the actual record data fields.\n\nThe structure of this object will vary depending on the specific\nexport configuration and the source system's data structure.\n","additionalProperties":true}}}}}},"Tool":{"type":"object","required":["_id","name","_integrationId","createdAt","lastModified"],"description":"Tool object as returned by the API.","allOf":[{"$ref":"#/components/schemas/ToolBase"},{"$ref":"#/components/schemas/ResourceResponse"},{"type":"object","properties":{"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Origin resource ID when this tool was created by cloning or installing a template."},"draftExpiresAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when a draft tool auto-deletes. Server-computed when `draft` is set at\ncreation."}}}]},"ToolBase":{"type":"object","description":"Writable tool fields shared by the request and response schemas.","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Human-readable name for the tool.\n\nDisplayed in the UI and used to identify the tool's purpose.\n"},"description":{"type":"string","maxLength":5120,"description":"Optional detailed description of what the tool does.\n\nUse this to document the tool's purpose, expected inputs/outputs,\nand any special considerations.\n"},"_integrationId":{"type":"string","format":"objectId","description":"Reference to the integration this tool belongs to.\n\nEvery tool must be associated with an integration. The integration\ndetermines the scope and access controls for the tool.\n"},"input":{"$ref":"#/components/schemas/Input"},"output":{"$ref":"#/components/schemas/Output"},"routers":{"type":"array","description":"Optional routers for conditional processing logic.\n\nRouters allow you to direct input data to different processing branches\nbased on filter criteria or script logic. Tools only support\n\"first_matching_branch\" routing strategy.\n\nBranches can chain to other routers or use the special \"outputRouter\"\nterminal value to exit the tool.\n","items":{"$ref":"#/components/schemas/Router"}},"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"draft":{"type":"boolean","description":"When true, this tool is a draft that auto-deletes when its expiry passes\n(`draftExpiresAt` in the response). Set at creation; an update can clear the\nflag but never set it."}}},"Input":{"type":"object","description":"Configuration for the tool's input processing.\n\nDefines the expected input structure, optional transformations to apply\nbefore routing, and mock data for testing.\n","properties":{"name":{"type":"string","maxLength":200,"description":"Display name for the input configuration.\n"},"description":{"type":"string","maxLength":10240,"description":"Description of the expected input data and its purpose.\n"},"schema":{"type":"object","description":"JSON Schema describing the expected input data structure.\n\nUsed for validation, documentation, and AI-assisted tooling.\nMust be a valid JSON Schema document.\n","additionalProperties":true},"transform":{"$ref":"#/components/schemas/Transform"},"debugUntil":{"type":"string","format":"date-time","description":"Enables debug capture for the tool input stage until this timestamp.\nWhile it is in the future, invocations write input-stage\nrequest/response logs, listed at\n`GET /v1/tools/{_id}/tool_input/requests`. At most 1 hour in the\nfuture. A past timestamp (or omitting the field) stops capture.\nArm/disarm with `PATCH /v1/tools/{_id}` path `/input/debugUntil`.\n"},"mockInput":{"type":"object","description":"Mock data for testing the tool's input processing.\n\nProvides sample input to test transformation logic and routing\nwithout requiring live data. Maximum size: 1MB.\n","additionalProperties":true}}},"Output":{"type":"object","description":"Configuration for the tool's output processing.\n\nDefines how the tool's results are mapped, transformed, and enriched\nbefore being returned. Supports field mappings, lookups for data\nenrichment, and custom script hooks for pre/post-mapping processing.\n","properties":{"name":{"type":"string","maxLength":200,"description":"Display name for the output configuration.\n"},"description":{"type":"string","maxLength":10240,"description":"Description of the output data and its purpose.\n"},"schema":{"type":"object","description":"JSON Schema describing the output data structure.\n\nUsed for documentation and validation of the tool's output.\nMust be a valid JSON Schema document.\n","additionalProperties":true},"mappings":{"description":"Field mappings to transform data into the output format.\n\nMaps data from processing results to the output structure.\nUses Celigo's standard mapping format with extract/generate field paths —\na flat array of mapping entries (each entry may recurse via its own\nnested ``mappings`` for object/array structures).\n","allOf":[{"$ref":"#/components/schemas/Mappings"}]},"lookups":{"type":"array","description":"Lookup tables for data enrichment during output processing.\n\nStatic key-value mappings used to translate values (e.g., status codes,\ncategory names) during output generation.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Name of the lookup, used to reference it from mappings.\n"},"map":{"type":"object","description":"Key-value mapping object. Keys are the input values and\nvalues are the corresponding output values.\n","additionalProperties":true},"default":{"type":"string","description":"Default value returned when the input key is not found in the map.\n"},"allowFailures":{"type":"boolean","description":"Whether to continue processing if the lookup fails to find a match\nand no default is provided.\n"}}}},"hooks":{"type":"object","description":"Custom script hooks for pre- and post-mapping processing.\n\nAllows running custom JavaScript functions before and after\noutput mappings are applied.\n","properties":{"preMap":{"type":"object","description":"Script to run before applying output mappings.\n\nCan modify the data before it is mapped to the output structure.\n","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name to execute within the script"}}},"postMap":{"type":"object","description":"Script to run after applying output mappings.\n\nCan modify the final output data after mappings are applied.\n","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name to execute within the script"}}}}},"debugUntil":{"type":"string","format":"date-time","description":"Enables debug capture for the tool output stage until this timestamp.\nWhile it is in the future, invocations write output-stage\nrequest/response logs, listed at\n`GET /v1/tools/{_id}/tool_output/requests`. At most 1 hour in the\nfuture. A past timestamp (or omitting the field) stops capture.\nArm/disarm with `PATCH /v1/tools/{_id}` path `/output/debugUntil`.\n"},"mockInput":{"type":"object","description":"Mock data for testing the tool's output processing.\n\nProvides sample data that would arrive from the routing/processing\nstage, used to test mapping and lookup logic. Maximum size: 1MB.\n","additionalProperties":true}}},"Router":{"type":"object","description":"Configuration for conditional routing within a tool.\n\nRouters evaluate input data and direct it to different processing branches\nbased on criteria. This enables complex business logic and conditional\nprocessing within the tool.\n\nUnlike flows, tools only support \"first_matching_branch\" routing strategy.\nBranches can chain to other routers or use the special \"outputRouter\"\nterminal sink to exit the tool and return results.\n","properties":{"id":{"type":"string","description":"Unique identifier for this router within the tool.\n\nUsed to reference this router from other routers' branch `nextRouterId`.\n"},"name":{"type":"string","maxLength":300,"description":"Human-readable name for the router.\n"},"routeRecordsTo":{"type":"string","enum":["first_matching_branch"],"description":"Routing strategy. Tools only support \"first_matching_branch\",\nwhich routes to the first branch whose criteria match the input.\n"},"routeRecordsUsing":{"type":"string","enum":["input_filters","script"],"description":"Method used to evaluate routing criteria.\n\n- **input_filters**: Use declarative filter expressions on each branch\n- **script**: Use a custom JavaScript function to determine the branch\n"},"script":{"type":"object","description":"Script configuration when routeRecordsUsing is \"script\".\n\nThe function should return the name of the branch to route to.\n","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name that returns the branch name"}}},"branches":{"type":"array","description":"List of branches defining different processing paths.\n\nEach branch has optional filter criteria and a set of processing steps.\nRecords are evaluated against branch criteria in order; the first\nmatching branch is selected.\n","items":{"type":"object","properties":{"name":{"type":"string","maxLength":300,"description":"Name of this branch.\n"},"description":{"type":"string","maxLength":10240,"description":"Description of when and why this branch is selected.\n"},"branchId":{"type":"string","description":"Stable identifier for this branch within the tool, generated by\nthe builder. Used to reference the branch independently of its\nposition in the branches array (e.g., from step requests).\n"},"inputFilter":{"type":"object","description":"Filter criteria to determine if this branch should be selected.\n\nUses Celigo's expression-based filter format.\n","properties":{"version":{"type":"string","enum":["1"],"description":"Filter version"},"rules":{"type":"array","description":"Filter rules in Celigo expression-based filter format.\n\nArray-based DSL where the first element is an operator (e.g., \"equals\", \"and\", \"or\"),\nfollowed by operands which can be nested expressions.\n","items":{}}}},"nextRouterId":{"type":"string","description":"Identifier of the next router to chain to after this branch completes.\n\nUse \"outputRouter\" as a special terminal value to exit the tool\nand return the processing results.\n"},"pageProcessors":{"type":"array","description":"Processing steps to execute in this branch.\n\nEach processor references an export (lookup) or import resource\nfor data retrieval or submission.\n","items":{"type":"object","properties":{"type":{"type":"string","enum":["export","import"],"description":"Type of processor.\n\n- **export**: Retrieves data from an external system (lookup)\n- **import**: Sends data to an external system\n"},"_exportId":{"type":"string","format":"objectId","description":"Export resource reference (when type is \"export\")"},"_importId":{"type":"string","format":"objectId","description":"Import resource reference (when type is \"import\")"},"proceedOnFailure":{"type":"boolean","description":"Whether to continue processing subsequent steps if this\nprocessor fails.\n"},"setupInProgress":{"type":"boolean","description":"When true, the processor's configuration is still being\nset up in the UI and the step is not yet runnable.\n"},"responseMapping":{"type":"object","description":"Merges fields from this processor's response back onto the\nin-flight record so later processors and the tool's output\ncan read them. Extracts do NOT read the raw application\nresponse — they evaluate against the platform's canonical\nper-record envelope: for lookups (`type: \"export\"`) that is\n`{\"statusCode\", \"data\": [<result records>], \"errors\"}`, so\npaths must start from `data` (e.g. `data[0].name`); for\nimports it is `{\"id\", \"statusCode\", \"ignored\", \"_json\"}`,\nso use `id` or `_json.<path>`. Bare result-record field\nnames resolve to nothing and merge nothing.\n","properties":{"fields":{"type":"array","description":"Simple field-level mappings","items":{"type":"object","properties":{"extract":{"type":"string","description":"Path within the canonical response envelope to\ncopy the value from (`data[0].x` / `data.0.x`\nfor lookups; `id` or `_json.<path>` for\nimports).\n"},"generate":{"type":"string","description":"Field path on the in-flight record where the\nextracted value is stored (dot notation for\nnesting).\n"}}}},"lists":{"type":"array","description":"List-level mappings for array data","items":{"type":"object","properties":{"generate":{"type":"string","description":"Target list path"},"fields":{"type":"array","description":"Field-level mappings applied to each item in the list.","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path"},"generate":{"type":"string","description":"Target field path"}}}}}}}}},"hooks":{"type":"object","description":"Custom scripts for processing","properties":{"postResponseMap":{"type":"object","description":"Script to run after response mapping","properties":{"_scriptId":{"type":"string","format":"objectId","description":"Reference to the script resource"},"function":{"type":"string","description":"Function name to execute"}}}}}}}}}}}}},"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/tools/{_id}/descendants":{"get":{"summary":"List resources a tool depends on, grouped by type","operationId":"listToolDescendants","tags":["Tools"],"description":"Returns the full dependency tree of a tool as three arrays: the\n`imports`, `exports`, and nested `tools` it references directly or\ntransitively. Each entry is the complete resource document, so the\ncaller doesn't need to fan out individual GETs.\n\nPair with `GET /v1/tools/{_id}/connections` to enumerate the full\nresource and connection footprint in two calls.","parameters":[{"name":"_id","in":"path","required":true,"description":"Tool id.","schema":{"type":"string","format":"objectId"}}],"responses":{"200":{"description":"Full descendant resource docs grouped by type. Each array may be\nempty when the tool doesn't reference that resource kind.","content":{"application/json":{"schema":{"type":"object","properties":{"imports":{"type":"array","description":"Full Import documents the tool depends on.","items":{"$ref":"#/components/schemas/Import"}},"exports":{"type":"array","description":"Full Export documents the tool depends on.","items":{"$ref":"#/components/schemas/Export"}},"tools":{"type":"array","description":"Full inner Tool documents nested beneath this tool.","items":{"$ref":"#/components/schemas/Tool"}}}}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
````

## Clone a tool

> Creates a copy of a tool in the target integration and returns a manifest of the\
> resources the clone created. The clone records its lineage in \`\_sourceId\`, which\
> places it in the source tool's clone family.\
> \
> The target integration is never inferred from the source tool — pass the source\
> tool's own integration id to clone in place.\
> \
> Use \`GET /v1/tools/{\_id}/clone/preview\` first to see what the clone would create.

```json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"tags":[{"name":"Tools","description":"Tools are reusable processing units within integrations that encapsulate input\ntransformation, conditional routing, output mapping, and data enrichment logic behind\nan input/output contract. They can be referenced from flows, APIs, AI agents, MCP\nservers, and other tools to promote modularity and reuse.\n\n## Tool schema\n\n{% openapi-schemas spec=\"tool\" schemas=\"Tool\" 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":{"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/tools/{_id}/clone":{"post":{"operationId":"cloneTool","tags":["Tools"],"summary":"Clone a tool","description":"Creates a copy of a tool in the target integration and returns a manifest of the\nresources the clone created. The clone records its lineage in `_sourceId`, which\nplaces it in the source tool's clone family.\n\nThe target integration is never inferred from the source tool — pass the source\ntool's own integration id to clone in place.\n\nUse `GET /v1/tools/{_id}/clone/preview` first to see what the clone would create.","parameters":[{"name":"_id","in":"path","required":true,"description":"The id of the tool to clone.","schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["_integrationId"],"properties":{"_integrationId":{"type":"string","format":"objectId","description":"Integration the cloned tool is created in."},"name":{"type":"string","description":"Name for the cloned tool. Defaults to `Clone - <source tool name>` when omitted."}}}}}},"responses":{"201":{"description":"The clone was created. Returns a manifest of the resources the clone created.","content":{"application/json":{"schema":{"type":"array","description":"Manifest of resources created by the clone.","items":{"type":"object","properties":{"model":{"type":"string","description":"Model name of the created resource (e.g. `Tool`)."},"_id":{"type":"string","format":"objectId","description":"Unique id of the created resource."}}}}}}},"400":{"description":"The body omits `_integrationId` (`required_field_missing`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
```

## Preview cloning a tool

> Returns a preview of the resources that would be created by cloning this tool. No\
> resources are created.\
> \
> Call this before \`POST /v1/tools/{\_id}/clone\` to inspect what the clone would create.

```json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"tags":[{"name":"Tools","description":"Tools are reusable processing units within integrations that encapsulate input\ntransformation, conditional routing, output mapping, and data enrichment logic behind\nan input/output contract. They can be referenced from flows, APIs, AI agents, MCP\nservers, and other tools to promote modularity and reuse.\n\n## Tool schema\n\n{% openapi-schemas spec=\"tool\" schemas=\"Tool\" 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/tools/{_id}/clone/preview":{"get":{"operationId":"previewCloneTool","tags":["Tools"],"summary":"Preview cloning a tool","description":"Returns a preview of the resources that would be created by cloning this tool. No\nresources are created.\n\nCall this before `POST /v1/tools/{_id}/clone` to inspect what the clone would create.","parameters":[{"name":"_id","in":"path","required":true,"description":"Tool id to preview cloning.","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"}}}}}}
```

## List dependencies of a tool

> 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 \`{}\` for both zero-dependency and nonexistent IDs.

```json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"tags":[{"name":"Tools","description":"Tools are reusable processing units within integrations that encapsulate input\ntransformation, conditional routing, output mapping, and data enrichment logic behind\nan input/output contract. They can be referenced from flows, APIs, AI agents, MCP\nservers, and other tools to promote modularity and reuse.\n\n## Tool schema\n\n{% openapi-schemas spec=\"tool\" schemas=\"Tool\" 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/tools/{_id}/dependencies":{"get":{"operationId":"listToolDependencies","tags":["Tools"],"summary":"List dependencies of a tool","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.\n\nReturns `{}` for both zero-dependency and 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"}}}}}}
```

## Get a downloadable template for a tool

> Packages the tool as an installable template and returns a signed S3 URL\
> where the template \`.zip\` can be downloaded. The URL is pre-signed and\
> short-lived (approximately 15 minutes), so fetch the file promptly; call\
> the endpoint again for a fresh URL.\
> \
> The \`.zip\` contains the tool definition plus every resource it references\
> — nested tools, exports, imports, connections, and scripts — grouped into\
> one folder per resource type, with an \`integration.json\` manifest at the\
> root. Requires the \`create:tool:template\` permission.

```json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"tags":[{"name":"Tools","description":"Tools are reusable processing units within integrations that encapsulate input\ntransformation, conditional routing, output mapping, and data enrichment logic behind\nan input/output contract. They can be referenced from flows, APIs, AI agents, MCP\nservers, and other tools to promote modularity and reuse.\n\n## Tool schema\n\n{% openapi-schemas spec=\"tool\" schemas=\"Tool\" 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"}}}}},"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/tools/{_id}/template":{"get":{"operationId":"getToolTemplate","tags":["Tools"],"summary":"Get a downloadable template for a tool","description":"Packages the tool as an installable template and returns a signed S3 URL\nwhere the template `.zip` can be downloaded. The URL is pre-signed and\nshort-lived (approximately 15 minutes), so fetch the file promptly; call\nthe endpoint again for a fresh URL.\n\nThe `.zip` contains the tool definition plus every resource it references\n— nested tools, exports, imports, connections, and scripts — grouped into\none folder per resource type, with an `integration.json` manifest at the\nroot. Requires the `create:tool:template` permission.","parameters":[{"name":"_id","in":"path","required":true,"description":"Tool id.","schema":{"type":"string","format":"objectId"}}],"responses":{"200":{"description":"Signed download URL for the tool template zip.","content":{"application/json":{"schema":{"type":"object","properties":{"signedURL":{"type":"string","format":"uri","description":"Pre-signed, short-lived S3 URL to download the template `.zip`."},"key":{"type":"string","description":"S3 object key for the generated template `.zip`, named `<toolId>.zip`."}}}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
```

## Invoke a Tool synchronously

> Executes a Tool synchronously and returns the mapped output (or errors).\
> \
> Optional \`x-log-mode\` enables enterprise invocation logging. When logging is\
> active for the run, the response includes \`invocationId\` — use that value as\
> \`{executionId}\` with \`GET /v1/tools/{\_id}/invocations/{executionId}\`.\
> Sandbox Tools return \`403\` when logging is attempted.

```json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"tags":[{"name":"Tools","description":"Tools are reusable processing units within integrations that encapsulate input\ntransformation, conditional routing, output mapping, and data enrichment logic behind\nan input/output contract. They can be referenced from flows, APIs, AI agents, MCP\nservers, and other tools to promote modularity and reuse.\n\n## Tool schema\n\n{% openapi-schemas spec=\"tool\" schemas=\"Tool\" 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"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"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/tools/{_id}/invoke":{"post":{"operationId":"invokeTool","tags":["Tools"],"summary":"Invoke a Tool synchronously","description":"Executes a Tool synchronously and returns the mapped output (or errors).\n\nOptional `x-log-mode` enables enterprise invocation logging. When logging is\nactive for the run, the response includes `invocationId` — use that value as\n`{executionId}` with `GET /v1/tools/{_id}/invocations/{executionId}`.\nSandbox Tools return `403` when logging is attempted.","parameters":[{"name":"_id","in":"path","required":true,"description":"The Tool id.","schema":{"type":"string","format":"objectId"}},{"name":"x-log-mode","in":"header","required":false,"description":"Enables enterprise Tool logging for this invoke. Omit to skip logging.\nEvery direct API call is a standalone invoke: only `debug` enables logging —\nthe other valid values are accepted but silently ignored (the invoke runs\nwithout logging; no error is returned). Values outside the enum return\n`400`. The `basic` and `detailed` levels (`standard` is an alias for\n`basic`) apply only to Agent, MCP, and Guardrail invocations, where Celigo\nservices forward the invoker's effective log level; external callers\ncannot select them.","schema":{"type":"string","enum":["basic","standard","detailed","debug"]}},{"name":"x-integration-id","in":"header","required":false,"description":"Invoker integration id for Agent or Guardrail invokes.\nIgnored for MCP and standalone invokes.","schema":{"type":"string","format":"objectId"}},{"name":"x-by-user-id","in":"header","required":false,"description":"End-user id that triggered an Agent or Guardrail invoke.\nIgnored for MCP and standalone invokes.","schema":{"type":"string","format":"objectId"}},{"name":"x-invoker-id","in":"header","required":false,"description":"Direct caller resource id for Agent or Guardrail invokes.\nIgnored for MCP and standalone invokes.","schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Tool invoke body. Both `input` and `overrides` are required — a missing\nbody or a missing key returns `400`. Pass `{}` for either when there is\nnothing to send.","required":["input","overrides"],"properties":{"input":{"type":"object","additionalProperties":true,"description":"Tool input data conforming to the Tool's input schema when defined."},"overrides":{"type":"object","description":"Runtime overrides for connections, imports, exports, and routers.","additionalProperties":true}},"additionalProperties":true}}}},"responses":{"200":{"description":"Tool executed successfully.","content":{"application/json":{"schema":{"type":"object","required":["output","errors"],"properties":{"output":{"type":"object","additionalProperties":true},"errors":{"type":"array","description":"Empty on success. Non-empty execution errors are returned as `422` instead.","items":{"type":"object","additionalProperties":true}},"invocationId":{"type":"string","format":"objectId","description":"Present when enterprise Tool logging is active for the run."}},"additionalProperties":true}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"404":{"$ref":"#/components/responses/404-not-found"},"422":{"description":"Tool invocation error. May include `invocationId` when logging is active.","content":{"application/json":{"schema":{"type":"object","required":["errors"],"properties":{"errors":{"type":"array","items":{"type":"object","additionalProperties":true}},"invocationId":{"type":"string","format":"objectId"}},"additionalProperties":true}}}}}}}}}
```

## Run a tool in test mode

> Synchronously executes a tool in test mode and returns the run metadata\
> together with the resulting flow job and its child jobs. Use this to\
> validate a tool's routing and step configuration before referencing it\
> from a flow, API, agent, or MCP server.\
> \
> The request body is optional. When supplied, wrap the test input in an\
> \`input\` key (\`{"input": {...}}\`) matching the tool's input contract. The\
> run does not read the tool's saved \`input.mockInput\` — without a wrapped\
> \`input\` the tool executes against an empty input record. The \`metadata\`\
> object in the response maps each step id to the ordered list of stage\
> names that ran for that step; use those ids with\
> \`GET /v1/tools/{\_id}/test/run/{runId}/{\_stepId}\` to inspect stage-by-stage\
> results. The run id for follow-up calls is the \`flowJob.\_id\` value.\
> \
> Test runs are a separate, short-lived history from normal runs — capture\
> any follow-up step or log details soon after the run completes.

```json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"tags":[{"name":"Tools","description":"Tools are reusable processing units within integrations that encapsulate input\ntransformation, conditional routing, output mapping, and data enrichment logic behind\nan input/output contract. They can be referenced from flows, APIs, AI agents, MCP\nservers, and other tools to promote modularity and reuse.\n\n## Tool schema\n\n{% openapi-schemas spec=\"tool\" schemas=\"Tool\" 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"}}}},"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/tools/{_id}/test/run":{"post":{"summary":"Run a tool in test mode","description":"Synchronously executes a tool in test mode and returns the run metadata\ntogether with the resulting flow job and its child jobs. Use this to\nvalidate a tool's routing and step configuration before referencing it\nfrom a flow, API, agent, or MCP server.\n\nThe request body is optional. When supplied, wrap the test input in an\n`input` key (`{\"input\": {...}}`) matching the tool's input contract. The\nrun does not read the tool's saved `input.mockInput` — without a wrapped\n`input` the tool executes against an empty input record. The `metadata`\nobject in the response maps each step id to the ordered list of stage\nnames that ran for that step; use those ids with\n`GET /v1/tools/{_id}/test/run/{runId}/{_stepId}` to inspect stage-by-stage\nresults. The run id for follow-up calls is the `flowJob._id` value.\n\nTest runs are a separate, short-lived history from normal runs — capture\nany follow-up step or log details soon after the run completes.","operationId":"testRunTool","tags":["Tools"],"parameters":[{"name":"_id","in":"path","required":true,"description":"The unique identifier of the tool to test.","schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","description":"Optional test input for the run. Send an empty object or omit\nthe body entirely to run with no input.","properties":{"input":{"type":"object","description":"The test input record the tool runs against. Shape depends\non the tool's input contract. The run does not fall back to\nthe tool's saved `input.mockInput` — when this key is\nabsent, the input step receives an empty record.","additionalProperties":true}},"additionalProperties":true}}}},"responses":{"200":{"description":"The tool ran. Returns the per-step stage metadata, the flow job that\nexecuted the tool, and the child jobs for each step.","content":{"application/json":{"schema":{"type":"object","description":"Test-run result envelope.","properties":{"metadata":{"type":"object","description":"Maps each step id to the ordered list of stage names that\nran for that step (e.g. `request`, `parse`, `router`,\n`input`). Empty array means the step ran no stages.","additionalProperties":{"type":"array","items":{"type":"string"}}},"flowJob":{"type":"object","description":"The flow job record produced by the test run.","additionalProperties":true},"childJobs":{"type":"array","description":"Child job records, one per executed step.","items":{"type":"object","additionalProperties":true}}},"required":["metadata","flowJob","childJobs"]}}}},"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"}}}}}}
```

## Get a tool test-run step result

> Returns the stage-by-stage result of a single step from a prior tool test\
> run. Each entry in \`stages\[]\` describes one stage (e.g. \`request\`,\
> \`parse\`, \`router\`, \`input\`) with its \`input\`, \`output\`, and any \`errors\`.\
> \
> The \`runId\` is the \`flowJob.\_id\` returned by\
> \`POST /v1/tools/{\_id}/test/run\`. The \`{\_stepId}\` is one of the step ids\
> found in that run's \`metadata\` map. Test-run history is separate from\
> normal run history and is short-lived — fetch step results soon after the\
> run completes.

```json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"tags":[{"name":"Tools","description":"Tools are reusable processing units within integrations that encapsulate input\ntransformation, conditional routing, output mapping, and data enrichment logic behind\nan input/output contract. They can be referenced from flows, APIs, AI agents, MCP\nservers, and other tools to promote modularity and reuse.\n\n## Tool schema\n\n{% openapi-schemas spec=\"tool\" schemas=\"Tool\" 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/tools/{_id}/test/run/{runId}/{_stepId}":{"get":{"summary":"Get a tool test-run step result","description":"Returns the stage-by-stage result of a single step from a prior tool test\nrun. Each entry in `stages[]` describes one stage (e.g. `request`,\n`parse`, `router`, `input`) with its `input`, `output`, and any `errors`.\n\nThe `runId` is the `flowJob._id` returned by\n`POST /v1/tools/{_id}/test/run`. The `{_stepId}` is one of the step ids\nfound in that run's `metadata` map. Test-run history is separate from\nnormal run history and is short-lived — fetch step results soon after the\nrun completes.","operationId":"getToolTestRunStep","tags":["Tools"],"parameters":[{"name":"_id","in":"path","required":true,"description":"The unique identifier of the tool.","schema":{"type":"string","format":"objectId"}},{"name":"runId","in":"path","required":true,"description":"Test run id from the `POST /v1/tools/{_id}/test/run` response\n(`flowJob._id`). Distinct from normal flow-run Job ids.","schema":{"type":"string"}},{"name":"_stepId","in":"path","required":true,"description":"Id of the step whose stage results you want. Find step ids in the\ntest-run `metadata` map.","schema":{"type":"string"}}],"responses":{"200":{"description":"Stage-by-stage result for the step. `stages[]` carries per-stage\n`input`, `output`, and `errors`; the top-level `errors` array\naggregates step-level errors.","content":{"application/json":{"schema":{"type":"object","description":"Step result envelope.","properties":{"stages":{"type":"array","description":"Ordered list of stages that ran for the step.","items":{"type":"object","description":"One stage of the step.","properties":{"name":{"type":"string","description":"Stage name (e.g. `request`, `parse`, `router`, `input`)."},"errors":{"description":"Stage errors, or `null` when the stage had none.","type":["array","null"],"items":{"type":"object","additionalProperties":true}},"output":{"description":"Stage output records, or `null` when the stage produced none.","type":["array","null"],"items":{"type":"object","additionalProperties":true}},"input":{"description":"Stage input records, or `null` when the stage consumed none.","type":["array","null"],"items":{"type":"object","additionalProperties":true}}}}},"errors":{"type":"array","description":"Step-level errors aggregated across stages.","items":{"type":"object","additionalProperties":true}}},"required":["stages","errors"]}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
```

## Get request/response logs for a tool test-run step

> Returns the outbound HTTP request/response log pairs captured during a\
> specific step of a tool test run. Only steps that issued outbound HTTP\
> calls (exports, imports, lookups) produce entries — routers, filters, and\
> other in-process stages return \`404\` with code \`req\_res\_logs\_not\_found\`\
> by design.\
> \
> The \`runId\` is the \`flowJob.\_id\` returned by\
> \`POST /v1/tools/{\_id}/test/run\`; the \`{\_stepId}\` is the export or import\
> id of the step you want logs for, found in the test-run \`metadata\` map.\
> \
> Response entries may carry base64-encoded JSON in \`request.body\` and\
> \`response.body\` — decode string bodies before parsing. Test runs are\
> short-lived ephemeral state, so capture logs soon after the run completes.

```json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"tags":[{"name":"Tools","description":"Tools are reusable processing units within integrations that encapsulate input\ntransformation, conditional routing, output mapping, and data enrichment logic behind\nan input/output contract. They can be referenced from flows, APIs, AI agents, MCP\nservers, and other tools to promote modularity and reuse.\n\n## Tool schema\n\n{% openapi-schemas spec=\"tool\" schemas=\"Tool\" 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"]}}}}},"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/tools/{_id}/test/run/{runId}/{_stepId}/logs/requestAndResponse":{"get":{"summary":"Get request/response logs for a tool test-run step","description":"Returns the outbound HTTP request/response log pairs captured during a\nspecific step of a tool test run. Only steps that issued outbound HTTP\ncalls (exports, imports, lookups) produce entries — routers, filters, and\nother in-process stages return `404` with code `req_res_logs_not_found`\nby design.\n\nThe `runId` is the `flowJob._id` returned by\n`POST /v1/tools/{_id}/test/run`; the `{_stepId}` is the export or import\nid of the step you want logs for, found in the test-run `metadata` map.\n\nResponse entries may carry base64-encoded JSON in `request.body` and\n`response.body` — decode string bodies before parsing. Test runs are\nshort-lived ephemeral state, so capture logs soon after the run completes.","operationId":"getToolTestRunStepLogs","tags":["Tools"],"parameters":[{"name":"_id","in":"path","required":true,"description":"The unique identifier of the tool.","schema":{"type":"string","format":"objectId"}},{"name":"runId","in":"path","required":true,"description":"Test run id from the `POST /v1/tools/{_id}/test/run` response\n(`flowJob._id`). Distinct from normal flow-run Job ids.","schema":{"type":"string"}},{"name":"_stepId","in":"path","required":true,"description":"Export or import id of the step whose logs you want. Non-HTTP stages\n(routers, filters) 404 with `req_res_logs_not_found`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Array of request/response log pairs captured during the step.\n`request.body` / `response.body` may be base64-encoded JSON.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","description":"One request/response pair captured by the test engine.","properties":{"request":{"type":"object","description":"Outbound HTTP request envelope.","properties":{"method":{"type":"string","description":"HTTP method (e.g. `GET`, `POST`)."},"url":{"type":"string","description":"Fully-resolved request URL."},"headers":{"type":"object","description":"Request headers; sensitive values redacted server-side.","additionalProperties":true},"body":{"description":"Request body. Often **base64-encoded JSON** — decode\nbefore parsing. Empty for bodyless methods.","type":["string","null"]}}},"response":{"type":"object","description":"Remote response envelope.","properties":{"statusCode":{"type":"integer","description":"HTTP status code returned by the remote."},"headers":{"type":"object","description":"Response headers.","additionalProperties":true},"body":{"description":"Response body. Often **base64-encoded JSON** — decode\nbefore parsing.","type":["string","null"]}}}}}}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"description":"Either the run/step id didn't resolve, or the step produced no\nrequest/response logs (non-HTTP stage — router, filter, etc.).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## List captured debug requests for a tool step

> Lists the debug request records captured for a specific step of a tool.\
> These are the raw request/response payloads the step processed, retained\
> for troubleshooting while debug capture is active for the step. Use the\
> \`key\` of an entry with \`GET /v1/tools/{\_id}/{\_stepId}/requests/{key}\` to\
> fetch the full detail of a single captured request.\
> \
> When more records match than fit in one page, \`nextPageURL\` returns the\
> relative URL for the next page.\
> \
> \*\*Important:\*\* Time parameters (\`time\_lte\`, \`time\_gt\`) must be epoch\
> milliseconds (integers), not ISO 8601 strings. When \`time\_lte\` is\
> provided, \`time\_gt\` is also required.\
> \
> Requires the \`manage:tool:logs\` permission. Returns \`{requests: \[]}\` when\
> the step has captured no debug requests.

```json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"tags":[{"name":"Tools","description":"Tools are reusable processing units within integrations that encapsulate input\ntransformation, conditional routing, output mapping, and data enrichment logic behind\nan input/output contract. They can be referenced from flows, APIs, AI agents, MCP\nservers, and other tools to promote modularity and reuse.\n\n## Tool schema\n\n{% openapi-schemas spec=\"tool\" schemas=\"Tool\" 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"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"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/tools/{_id}/{_stepId}/requests":{"get":{"summary":"List captured debug requests for a tool step","description":"Lists the debug request records captured for a specific step of a tool.\nThese are the raw request/response payloads the step processed, retained\nfor troubleshooting while debug capture is active for the step. Use the\n`key` of an entry with `GET /v1/tools/{_id}/{_stepId}/requests/{key}` to\nfetch the full detail of a single captured request.\n\nWhen more records match than fit in one page, `nextPageURL` returns the\nrelative URL for the next page.\n\n**Important:** Time parameters (`time_lte`, `time_gt`) must be epoch\nmilliseconds (integers), not ISO 8601 strings. When `time_lte` is\nprovided, `time_gt` is also required.\n\nRequires the `manage:tool:logs` permission. Returns `{requests: []}` when\nthe step has captured no debug requests.","operationId":"listToolStepRequests","tags":["Tools"],"parameters":[{"name":"_id","in":"path","required":true,"description":"The unique identifier of the tool.","schema":{"type":"string","format":"objectId"}},{"name":"_stepId","in":"path","required":true,"description":"The step whose captured logs to list. Either the ObjectId of an\nexport or import step defined directly in this tool's routers, or one\nof the virtual step ids `tool_input` / `tool_output` (logs captured\nvia `input.debugUntil` / `output.debugUntil`). Steps nested inside a\nchild tool are addressed on that child tool's id. Reserved path\nsegments such as `invocations`, `invoke`, and `test` are not valid\nstep ids.","schema":{"type":"string"}},{"name":"time_gt","in":"query","required":false,"description":"Lower bound of the time window (exclusive), as epoch milliseconds.\nRequired when `time_lte` is provided.","schema":{"type":"integer","format":"int64"}},{"name":"time_lte","in":"query","required":false,"description":"Upper bound of the time window (inclusive), as epoch milliseconds.\nDefaults to now if omitted. When provided, `time_gt` is also required.","schema":{"type":"integer","format":"int64"}},{"name":"method","in":"query","required":false,"description":"Only return records with these HTTP methods. Repeat the parameter to\nfilter on multiple values.","style":"form","explode":true,"schema":{"type":"array","items":{"type":"string"}}},{"name":"statusCode","in":"query","required":false,"description":"Only return records with these response status codes. Repeat the\nparameter to filter on multiple values.","style":"form","explode":true,"schema":{"type":"array","items":{"type":"string"}}},{"name":"stage","in":"query","required":false,"description":"Only return records for these processing stages. Repeat the parameter\nto filter on multiple values.","style":"form","explode":true,"schema":{"type":"array","items":{"type":"string"}}},{"name":"nextPageToken","in":"query","required":false,"description":"Continuation token for pagination. Do not construct this manually —\nfollow the `nextPageURL` returned by the previous page instead.","schema":{"type":"string"}}],"responses":{"200":{"description":"Captured debug request records for the step. Empty `requests` array\nwhen none were captured.","content":{"application/json":{"schema":{"type":"object","description":"Debug request list envelope.","properties":{"requests":{"type":"array","description":"Captured debug request records.","items":{"type":"object","description":"Summary of one captured debug request.","properties":{"key":{"type":"string","description":"Identifier of the captured record; pass it to\n`GET /v1/tools/{_id}/{_stepId}/requests/{key}`."},"time":{"type":"integer","description":"Capture time as epoch milliseconds."},"method":{"type":"string","description":"HTTP method of the captured request."},"statusCode":{"type":"string","description":"Response status code of the captured request."},"stage":{"type":"string","description":"Processing stage the record was captured in."}},"additionalProperties":true}},"nextPageURL":{"type":"string","description":"Relative URL of the next page. Present only when more\nrecords match than were returned."}},"required":["requests"]}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"404":{"$ref":"#/components/responses/404-not-found"},"422":{"description":"Invalid time filter parameters. Error code: `invalid_or_missing_field`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Delete captured debug requests for a tool step

> Deletes captured debug request records for a specific step of a tool, by\
> their \`key\` values from \`GET /v1/tools/{\_id}/{\_stepId}/requests\`. Between\
> 1 and 1000 keys can be deleted per call.\
> \
> The response lists the keys that were deleted and, separately, any keys\
> that failed to delete along with the failure reason.\
> \
> Requires the \`manage:tool:logs:deletion\` permission.

```json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"tags":[{"name":"Tools","description":"Tools are reusable processing units within integrations that encapsulate input\ntransformation, conditional routing, output mapping, and data enrichment logic behind\nan input/output contract. They can be referenced from flows, APIs, AI agents, MCP\nservers, and other tools to promote modularity and reuse.\n\n## Tool schema\n\n{% openapi-schemas spec=\"tool\" schemas=\"Tool\" 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"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"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/tools/{_id}/{_stepId}/requests":{"delete":{"summary":"Delete captured debug requests for a tool step","description":"Deletes captured debug request records for a specific step of a tool, by\ntheir `key` values from `GET /v1/tools/{_id}/{_stepId}/requests`. Between\n1 and 1000 keys can be deleted per call.\n\nThe response lists the keys that were deleted and, separately, any keys\nthat failed to delete along with the failure reason.\n\nRequires the `manage:tool:logs:deletion` permission.","operationId":"deleteToolStepRequests","tags":["Tools"],"parameters":[{"name":"_id","in":"path","required":true,"description":"The unique identifier of the tool.","schema":{"type":"string","format":"objectId"}},{"name":"_stepId","in":"path","required":true,"description":"The step whose captured logs to delete. Either the ObjectId of an\nexport or import step defined directly in this tool's routers, or one\nof the virtual step ids `tool_input` / `tool_output` (logs captured\nvia `input.debugUntil` / `output.debugUntil`).","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Keys of the captured debug requests to delete.","properties":{"keys":{"type":"array","description":"Keys of the records to delete, from the\n`GET /v1/tools/{_id}/{_stepId}/requests` listing.","minItems":1,"maxItems":1000,"items":{"type":"string"}}},"required":["keys"]}}}},"responses":{"200":{"description":"Deletion outcome. `deleted` lists the removed keys; `errors` lists\nkeys that could not be removed with the failure reason.","content":{"application/json":{"schema":{"type":"object","description":"Deletion result envelope.","properties":{"deleted":{"type":"array","description":"Keys of the records that were deleted.","items":{"type":"string"}},"errors":{"type":"array","description":"Records that failed to delete.","items":{"type":"object","properties":{"key":{"type":"string","description":"Key of the record that failed to delete."},"error":{"type":"string","description":"Failure reason."}}}}},"required":["deleted","errors"]}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
```

## Get a captured debug request for a tool step

> Returns the full detail of a single captured debug request for a tool\
> step, identified by its \`key\`. Obtain the \`key\` from\
> \`GET /v1/tools/{\_id}/{\_stepId}/requests\`.\
> \
> The record carries the captured request/response payloads for the step,\
> plus the \`key\`, record \`id\`, and capture \`time\`. For \`tool\_input\`,\
> \`tool\_output\`, and ToolImport steps the stored body is the in-memory JSON\
> record (not adaptor-masked). Router Import/Export bubbles keep existing\
> adaptor masking.\
> \
> Requires the \`manage:tool:logs\` permission.

```json
{"openapi":"3.2.0","info":{"title":"Tools","version":"1.0.0"},"tags":[{"name":"Tools","description":"Tools are reusable processing units within integrations that encapsulate input\ntransformation, conditional routing, output mapping, and data enrichment logic behind\nan input/output contract. They can be referenced from flows, APIs, AI agents, MCP\nservers, and other tools to promote modularity and reuse.\n\n## Tool schema\n\n{% openapi-schemas spec=\"tool\" schemas=\"Tool\" 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"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"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/tools/{_id}/{_stepId}/requests/{key}":{"get":{"summary":"Get a captured debug request for a tool step","description":"Returns the full detail of a single captured debug request for a tool\nstep, identified by its `key`. Obtain the `key` from\n`GET /v1/tools/{_id}/{_stepId}/requests`.\n\nThe record carries the captured request/response payloads for the step,\nplus the `key`, record `id`, and capture `time`. For `tool_input`,\n`tool_output`, and ToolImport steps the stored body is the in-memory JSON\nrecord (not adaptor-masked). Router Import/Export bubbles keep existing\nadaptor masking.\n\nRequires the `manage:tool:logs` permission.","operationId":"getToolStepRequest","tags":["Tools"],"parameters":[{"name":"_id","in":"path","required":true,"description":"The unique identifier of the tool.","schema":{"type":"string","format":"objectId"}},{"name":"_stepId","in":"path","required":true,"description":"The step the captured request belongs to. Either the ObjectId of an\nexport or import step defined directly in this tool's routers, or one\nof the virtual step ids `tool_input` / `tool_output` (logs captured\nvia `input.debugUntil` / `output.debugUntil`). Steps nested inside a\nchild tool are addressed on that child tool's id. Reserved path\nsegments such as `invocations`, `invoke`, and `test` are not valid\nstep ids.","schema":{"type":"string"}},{"name":"key","in":"path","required":true,"description":"Key identifying the captured debug request, from the\n`GET /v1/tools/{_id}/{_stepId}/requests` listing.","schema":{"type":"string"}}],"responses":{"200":{"description":"The captured debug request detail.","content":{"application/json":{"schema":{"type":"object","description":"A single captured debug request record, including the captured\nrequest/response payloads.","properties":{"key":{"type":"string","description":"Echoes the `key` path parameter."},"id":{"type":"string","description":"Internal storage id parsed from the log filename."},"time":{"type":"integer","format":"int64","description":"Epoch milliseconds when this pair was stored."},"request":{"type":"object","description":"The captured request envelope. Synthetic `tool_input` /\n`tool_output` / ToolImport logs store the in-memory JSON\nrecord; router Import/Export bubbles store the adaptor HTTP\nrequest.","properties":{"method":{"type":"string","description":"HTTP method (`GET`, `POST`, …). Synthetic captures use `POST`."},"url":{"type":"string","description":"Fully-resolved URL sent. Present on adaptor HTTP\ncaptures; omitted on synthetic `tool_input` /\n`tool_output` / ToolImport logs."},"headers":{"type":"object","description":"Headers sent with the request. Adaptor HTTP captures\nmask credentials; synthetic captures have no adaptor\nmasking.","additionalProperties":true},"body":{"description":"Captured request payload. Object for synthetic\n`tool_input` / `tool_output` / ToolImport logs (the\nin-memory JSON record). String for adaptor HTTP\nrouter-bubble captures."}}},"response":{"type":"object","description":"The captured response envelope.","properties":{"statusCode":{"type":"integer","description":"HTTP status code (`200` on success, `422` on schema/transform failure for synthetic captures)."},"headers":{"type":"object","description":"Response headers. Empty object on synthetic captures that did not issue HTTP.","additionalProperties":true},"body":{"description":"Captured response payload. Object for synthetic\n`tool_input` / `tool_output` / ToolImport logs. String\nfor adaptor HTTP router-bubble captures."},"receivedAt":{"type":"integer","format":"int64","description":"Epoch milliseconds when this pair was captured. Present on synthetic logs."}}},"context":{"type":"object","description":"Run correlation for synthetic logs keyed by tool id. Present\non `tool_input` / `tool_output` / ToolImport captures so the\noriginating flow or job can be recovered; omitted on adaptor\nHTTP router-bubble logs.","properties":{"_rootContainerId":{"type":"string","description":"Owning flow, API, or tool id of the run that produced this capture."},"_jobId":{"type":"string","description":"Job id of the run that produced this capture."}}}},"additionalProperties":true}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
```


---

# 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/tools.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.
