> 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/ai-and-mcp/ai-agents.md).

# AI Agents

AI agents are LLM-powered import steps that classify, extract, validate, or generate data within integration flows. They are stored as import resources with `adaptorType: AiAgentImport` and managed via the `/v1/imports` endpoints.

Three built-in providers — OpenAI (via the Responses API), Google Gemini (via LiteLLM), and Anthropic Claude (via LiteLLM) — run on platform-managed credentials or, with a `_connectionId`, on your own key (BYOK). Catalog providers — Mistral, xAI, Hugging Face, DeepSeek, Cohere, and Groq — use one generic flat configuration format and always run on a BYOK connection.

### AI agent schema

## The AiAgent object

````json
{"openapi":"3.2.0","info":{"title":"AI Agents","version":"1.0.0"},"components":{"schemas":{"AiAgent":{"type":"object","required":["_id","name","adaptorType","aiAgent","createdAt","lastModified"],"description":"AI agent import object as returned by the API.","allOf":[{"$ref":"#/components/schemas/AiAgentBase"},{"$ref":"#/components/schemas/ResourceResponse"},{"type":"object","properties":{"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Export that feeds data into this import within a flow."},"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"apiIdentifier":{"type":"string","readOnly":true,"description":"Ten-character hex identifier for the resource."},"lookups":{"type":"array","readOnly":true,"description":"Lookup configurations associated with this import.","items":{"type":"object"}},"_templateId":{"type":"string","format":"objectId","readOnly":true,"description":"Template this AI agent was created from."},"draftExpiresAt":{"type":"string","format":"date-time","readOnly":true,"description":"When the draft version of this AI agent expires. Drafts are staged by Celigo's\nAI assistant when it proposes changes for review; this field is present only\nwhile an unapproved draft is pending."},"debugUntil":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp until which verbose debug logging runs for this AI agent. Read-only\non create/update; enable it through the debug PATCH endpoint, not a full update."}}}]},"AiAgentBase":{"type":"object","description":"Writable AI agent import fields shared by the request and response schemas. Set\n`aiAgent.provider` to choose the LLM backend, then configure the matching format:\n`aiAgent.openai` for OpenAI, `aiAgent.litellm` for Gemini and Anthropic Claude\n(with provider-specific settings under `aiAgent.litellm._overrides`), or the\ngeneric flat format (`aiAgent.model`, `aiAgent.modelOptions`, …) for the catalog\nproviders Mistral, xAI, Hugging Face, DeepSeek, Cohere, and Groq.","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Display name for the AI agent."},"description":{"type":"string","description":"Optional description of the AI agent's purpose."},"_connectionId":{"type":"string","format":"objectId","description":"Connection for BYOK (bring your own key) — an HTTP connection to the vendor's API.\nOptional for the built-in providers (`openai`, `gemini`, `anthropic`), which run on\nplatform-managed credentials when it is omitted. Catalog providers (`mistral`, `xai`,\n`huggingface`, `deepseek`, `cohere`, `groq`) have no platform-managed credentials: the save\nrequires it — without one the agent fails to save with 422 `connection_required` on\n`_connectionId`."},"_integrationId":{"type":"string","format":"objectId","description":"Integration this AI agent belongs to. Omitted for a standalone agent."},"adaptorType":{"type":"string","enum":["AiAgentImport"],"description":"Must be `AiAgentImport`."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this AI agent was created from, set when it is part of an installed integration app."},"aiAgent":{"$ref":"#/components/schemas/AiAgentConfig"},"mappings":{"allOf":[{"$ref":"#/components/schemas/Mappings"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Selects the data context that `mappings[].extract` evaluates against. With no\nmappings the agent receives the entire record as JSON (or only the record's own\ntop-level `text`, `record` or `files` fields, when it has any) and `inputContext`\nis moot. `record`\n(the default) extracts against the flat record; `envelope` extracts against the\nruntime envelope, which exposes the record alongside job, settings, and other\nrun-time metadata (`$.record.field`, `$.settings.field`, etc.). Same contract as\nthe import resource's `inputContext`."},"filter":{"description":"Filter applied to incoming records before they are sent to the LLM. Records that match\ncontinue through the agent; records that don't skip it, counted as ignored with no model\ncall, and continue to the next step.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"responseTransform":{"description":"Transformation that reshapes the LLM's response after records are processed, before the\nresponse is handled by response mappings and downstream steps.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the agent's import lifecycle.\nAI agents do not aggregate records, so the `postAggregate` hook that other import\ntypes expose is intentionally omitted here.","properties":{"preMap":{"description":"Hook that runs on each page of records before the agent's mappings are applied.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postMap":{"description":"Hook that runs after mappings are applied, before records are sent to the LLM.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postSubmit":{"description":"Hook that runs after the LLM responds, with both the submitted records and the responses.","allOf":[{"$ref":"#/components/schemas/Hook"}]}}},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"distributed":{"type":"boolean","description":"Always false for AI agent imports — the agent executes on Celigo's servers, not inside a target application."},"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 are silently skipped. Rarely meaningful for AI agents; accepted for import-surface parity."},"ignoreMissing":{"type":"boolean","description":"When true, records that do not already exist in the destination are silently skipped. Rarely meaningful for AI agents; accepted for import-surface parity."},"mockResponse":{"type":"array","description":"Predefined response records used in place of calling the LLM when testing the agent,\nso flows can run without spending tokens. Records must be in integrator.io canonical\nformat — 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 for structured-output agents."},"_text":{"type":"string","description":"Mock response body for text-output agents."},"_headers":{"type":"object","description":"Mock response headers."},"blobKey":{"type":"string","description":"Mock output of an agent with `blob` output (image generation:\n`openai.output.format.type` or `litellm.responseFormat.type` set to `blob`),\nwhich returns the stored image's key here. Unrelated to the import-level `blob`\nflag, which agents refuse."}}}}}},"AiAgentConfig":{"type":"object","description":"AI agent configuration for an AiAgentImport. An `ai_agent` guardrail is configured by\nthe guardrail spec's `GuardrailAiAgentConfig` instead, which has no `tools` arrays.\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 the\nsave requires one — without it the agent fails to save with 422\n`connection_required` on `_connectionId`.\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","max"],"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":"Caps the length of the model's response, in tokens. An agent created without it is\nsaved with 5000; raise it for long outputs such as large extractions."},"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":"Which editor the UI opens `jsonSchema` in; the schema sent to the provider\nis the same either way, and the field is only meaningful when `type` is\n\"json_schema\". Unset opens the visual editor, and an edit there rebuilds\n`jsonSchema` from the field list, keeping only `type`, `properties`,\n`required`, `items`, `description`, `enum`, `pattern`, length and numeric\nbounds, `multipleOf`, `minItems`/`maxItems` and a `\"null\"` type — other\nkeywords are dropped without a prompt, and `integer` becomes `number`."},"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`."}}},"schema":{"type":"object","readOnly":true,"additionalProperties":true,"description":"Server-written copy of `jsonSchema`, returned whenever `jsonSchema` is set — on\nAI agent imports and on guardrail agents alike. Send `jsonSchema`, not this field."}},"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","code_execution","tool"],"description":"Type of tool. Every type except `code_execution` is configured through the\nsibling object of the same name; OpenAI spells this one `code_execution`, while\nthe Gemini and Anthropic overrides spell theirs `codeExecution` — the other spelling\nfails with 422 `enum`."},"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"]}}}},"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":"Which editor the UI opens `jsonSchema` in; the schema sent to the provider is\nthe same either way, and the field is only meaningful when `type` is\n\"json_schema\". Unset opens the visual editor, and an edit there rebuilds\n`jsonSchema` from the field list, keeping only `type`, `properties`, `required`,\n`items`, `description`, `enum`, `pattern`, length and numeric bounds,\n`multipleOf`, `minItems`/`maxItems` and a `\"null\"` type — other keywords are\ndropped without a prompt, and `integer` becomes `number`."},"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","codeExecution","mcp","tool"],"description":"Type of Gemini tool. `codeExecution` is the Gemini spelling — `code_execution`\nfails with 422 `enum`."},"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"]}}}},"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","codeExecution"],"description":"Type of Claude tool. `codeExecution` is the Anthropic spelling — `code_execution`\nfails with 422 `enum`."},"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). Each\nentry is either a plain tool name or an object carrying display\nmetadata — same contract as the OpenAI and Gemini `allowedTools`.","items":{"oneOf":[{"title":"name","type":"string","description":"Tool name as exposed by the MCP server."},{"title":"tool","type":"object","required":["name"],"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."}}}]}}}},"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."}}}}}}}}},"Mappings":{"type":"array","description":"Array of field mapping configurations for transforming data from one format into another.\n\n**Guidance**\n\nThis schema is designed around RECURSION as its core architectural principle. Understanding this recursive\nnature is essential for building effective mappings:\n\n1. The schema is self-referential by design - a mapping can contain nested mappings of the same structure\n2. Complex data structures (nested objects and arrays of objects, to any depth) are ALL\n   handled through this recursive pattern; an array of arrays is not (build one in a script hook)\n3. Each mapping handles one level of the data structure; deeper levels are handled by nested mappings\n\nWhen generating mappings programmatically:\n- For simple fields (string, number, boolean): Create single mapping objects\n- For objects: Create a parent mapping with nested 'mappings' array containing child field mappings\n- For arrays: Use 'buildArrayHelper' with extract paths defining array inputs and\n  recursive 'mappings' to define object structures\n\nThe system will process these nested structures recursively during runtime, ensuring proper construction\nof complex hierarchical data while maintaining excellent performance.\n\nOn save, a leaf row with neither a source (`extract`, `hardCodedValue`, `lookupName`) nor a\ndestination (`generate`) is dropped silently, at every nesting level, and so is an object or\nobject-array row left with no source, no destination and no remaining child `mappings` or\n`buildArrayHelper` entries; a row with only one of the two, and a source-less, destination-less\nwrapper that still has children, are kept.\n\nThe shape of the array is the mapper's **JSON structure**. Named top-level items produce an object\nrecord (`record { }`). Exactly one top-level item with `dataType: objectarray` and no `generate` is\nthe `rows [ ]` structure: the mapped record is an array — usually one element per element of the\nsource array that root's `buildArrayHelper` iterates — Handlebars templates such as `http.body`\nbind it as `rows` instead of `record`, and the mapping editor shows that row's destination disabled.\n","items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}}},"items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}},"Filter":{"type":"object","description":"Configuration for selectively processing records based on specified criteria. This object enables\nprecise control over which items are included or excluded from processing operations.\n\n**Filter behavior**\n\nWhen configured, the filter is applied before processing begins:\n- Items that match the filter criteria are processed\n- Items that don't match are completely skipped\n- No partial processing is performed\n\nSend the whole object. A rules array sent alone as the `filter` value is not\nstored, and a full update of an export that carries one removes the filter the\nexport had, with no error.\n\n**Implementation approaches**\n\nThere are two distinct filtering mechanisms available:\n\n**Rule-Based Filtering (`type: \"expression\"`)**\n- **Best For**: Common filtering patterns based on standard attributes\n- **Capabilities**: Filter by names, values, dates, numerical ranges, text patterns\n- **Advantages**: Declarative, no coding required, consistent performance\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear, static criteria for selection\n\n**Script-Based Filtering (`type: \"script\"`)**\n- **Best For**: Complex logic, dynamic criteria, or business rules\n- **Capabilities**: Full programmatic control, access to complete metadata\n- **Advantages**: Maximum flexibility, can implement any filtering logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Simple rules aren't sufficient or logic needs to be dynamic\n","properties":{"type":{"type":"string","description":"Determines which filtering mechanism to use. This choice affects which properties\nmust be configured and how filtering logic is implemented.\n\n**Available types**\n\n**Rule-Based Filtering (`\"expression\"`)**\n- **Required Config**: The `expression` object with rule definitions\n- **Behavior**: Evaluates declarative rules against item attributes\n- **Best For**: Common patterns like name matching, date ranges, value limits\n- **Advantages**: Simpler to configure, no custom code required\n\n**Script-Based Filtering (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to determine which items to process\n- **Best For**: Complex conditions, business logic, dynamic criteria\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard filtering needs (name, size, date), use `\"expression\"`\n2. For complex logic or conditions not covered by expressions, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based filtering. This object enables filtering\nitems based on common attributes without requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define filtering rules that\ncan match against item attributes like name, type, value, date, and other properties.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Rules can be combined with AND/OR logic\n- Each rule can check a specific attribute\n- Multiple conditions can be applied (ranges, pattern matching, exact matches)\n\n**Common filter patterns**\n\n1. **Pattern matching**: Regular expressions with `matches`\n2. **Value range filtering**: Numbers between min and max values\n3. **Date range filtering**: Items created/modified within specific time ranges\n4. **Status checking**: Items with specific status values or properties\n\nFor AI agents: Rule-based filtering should be your first choice when the filtering criteria\ncan be expressed in terms of standard attributes. Only use script-based filtering when\nmore complex logic is required.\n","properties":{"version":{"type":"string","description":"Version identifier for the expression format. Currently only version \"1\" is supported.\n\nThis field ensures future compatibility if the expression format evolves. Always set to \"1\"\nfor current implementations.\n","enum":["1"]},"rules":{"type":"array","description":"Expression array defining filter conditions using prefix notation. The first element is the operator,\nfollowed by its operands which may themselves be nested expression arrays.\n\nThe rule expression follows this pattern:\n- First element: Operator name (string)\n- Remaining elements: Operands for that operator (values or nested expressions)\n\n**Expression structure**\n\nFilter expressions use a prefix notation where operators appear before their operands:\n```\n[operator, operand1, operand2, ...]\n```\n\n**Comparison Operators**\n- `\"equals\"`: Exact match (equals)\n- `\"notequals\"`: Not equal to value (not equals)\n- `\"greaterthan\"`: Value is greater than specified value (is greater than)\n- `\"greaterthanequals\"`: Value is greater than or equal to specified value (is greater than or equals)\n- `\"lessthan\"`: Value is less than specified value (is less than)\n- `\"lessthanequals\"`: Value is less than or equal to specified value (is less than or equals)\n- `\"startswith\"`: String starts with specified prefix (starts with)\n- `\"notstartswith\"`: String does not start with specified prefix (does not start with)\n- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"notendswith\"`: String does not end with specified suffix (does not end with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"empty\"`: Field is missing or empty (is empty)\n- `\"notempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches a regular expression, ignoring case (matches)\n\nOperator names are case-insensitive: `NOTEMPTY` and `endsWith` work and are stored\nas sent. `isempty`, `isnotempty`, `anyof`, `notcontains`, `doesnotstartwith`,\n`doesnotendwith`, `notmatches`, `doesnotmatch` and `notequal` are not operators:\nthey are refused with 422 \"Invalid operation: <name>\", with the name in lowercase,\nboth when the filter is saved and by `POST /v1/processors/filter`. To negate a\ncondition, wrap it in `not`.\n\nThe string comparisons (`equals`, `contains`, `startswith`, `endswith` and their\nnegations) are case-sensitive; wrap the field in `lowercase` to compare without case.\n`matches` is not: every pattern is compiled with the flags `gi`, so `^paid$` also\nmatches `PAID` and `[a-z]` matches capital letters. An inline flag such as `(?i)`\nmakes the pattern invalid. The filter still saves, but in a run every record fails\nwith `FILTER_EVALUATION_FAILED` (\"Invalid regular expression: /(?i)…/gi: Invalid\ngroup\"), and `POST /v1/processors/filter` refuses the call with 422 (live-verified\n2026-10-22).\n\n**Logical Operators**\n- `\"and\"`: All conditions must be true\n- `\"or\"`: At least one condition must be true\n- `\"not\"`: Negates the condition\n\n**Field Access and Type Conversion**\n- `\"extract\"`: Access a field from the item by name\n- `\"settings\"`: Access a custom setting from the flow, flow step, or integration configuration\n- `\"boolean\"`: Convert value to Boolean type\n- `\"epochtime\"`: Convert value to Epoch Time (Unix timestamp)\n- `\"number\"`: Convert value to Number type\n- `\"string\"`: Convert value to String type\n\n**Field Access Details**\n\n**Using `extract` to access record fields:**\n- Retrieves values from the current record being processed\n- Can access nested properties using dot notation (e.g., `\"customer.email\"`)\n- Paths are relative to the record itself: `[\"extract\", \"record.status\"]`\n  matches nothing\n- In a record filter, must be wrapped in a type conversion (`string`, `number`,\n  `boolean` or `epochtime`): a bare `[\"extract\", ...]` operand is refused with 422\n  \"Field operation extract must be called by a Type Conversion Operation but was called\n  by <operator>\", both when an import's filter is saved and by\n  `POST /v1/processors/filter`\n\n**Using `settings` to access configuration values:**\n- Retrieves values from the integration's configuration settings\n- Supports different scopes with prefix notation:\n  - `flow.settingName`: Access flow-level settings\n  - `flowGrouping.settingName`: Access the flow group's settings\n  - `export.settingName`: Access the export's (or lookup's) own settings\n  - `import.settingName`: Access the import's own settings\n  - `integration.settingName`: Access integration-level settings\n  - `connection.settingName`, `iClient.settingName`: Access the step's\n    connection and iClient settings\n- A scope whose resource has no settings can be absent; a rule that reads\n  it matches nothing\n- Must be wrapped in a type conversion like `extract`: a bare\n  `[\"settings\", ...]` operand is refused with 422 \"Field operation settings\n  must be called by a Type Conversion Operation\"\n- Useful for dynamic filtering based on configuration\n\n**Field Transformations**\n- `\"lowercase\"`: Convert string to lowercase\n- `\"uppercase\"`: Convert string to uppercase\n- `\"ceiling\"`: Round number up to the nearest integer\n- `\"floor\"`: Round number down to the nearest integer\n- `\"abs\"`: Get absolute value of a number\n\n**Arithmetic** (number operands)\n- `\"add\"`, `\"subtract\"`, `\"multiply\"`, `\"divide\"`, `\"modulo\"`: for example\n  `[\"subtract\", [\"number\", [\"extract\", \"total\"]], [\"number\", [\"extract\", \"discount\"]]]`.\n  `mod` is not an operator (422 \"Invalid operation: mod.\").\n\n**Dates**\n- `epochtime` reads ISO 8601 strings and epoch-millisecond numbers. An empty\n  string, a string that isn't a date, or epoch milliseconds sent as a string\n  fail it with \"Invalid date. Cannot convert given string or number to date\n  time.\", followed by `; record=` and the record as JSON: in a run that record fails (`FILTER_EVALUATION_FAILED`, source\n  `output_filter` on an export), and `POST /v1/processors/filter` and an\n  export preview refuse the whole call with 422. `and` and `or` evaluate\n  every operand, so a `notempty` test beside it doesn't prevent the failure.\n  A missing or null field doesn't match.\n- `datetime`, `date` and `timestamp` are not type conversions: 422\n  \"Invalid operation: <name>.\"\n- `number` reads a missing or empty field as 0.\n\n**Handlebars operands**\n- An operand can also be a string holding exactly one Handlebars expression, such as\n  `\"{{record.status}}\"` or `\"{{lowercase record.code}}\"`, on either side of the\n  operator. It renders against the record (`record.<field>`; an unprefixed\n  `{{status}}` renders empty) before the comparison.\n- The rendered value is a string: wrap it in `[\"number\", ...]` to compare it with a\n  number, or the rule is refused with 422.\n- `epochtime` does not render Handlebars: `[\"epochtime\", \"{{timestamp}}\"]` is refused\n  with 422 \"invalid datetime format\".\n- A string with text around the expression, or with more than one expression, is\n  compared as a literal.\n- `[\"number\", \"{{timestamp \\\"x\\\"}}\"]` is the current time in epoch milliseconds,\n  so `[\"greaterthanequals\", [\"epochtime\", [\"extract\", \"created\"]], [\"subtract\",\n  [\"number\", \"{{timestamp \\\"x\\\"}}\"], 604800000]]` keeps records created in the\n  last 7 days.\n- An unknown helper fails the rule with 422 \"Failed to evaluate handlebars\n  expression\".\n\nChoose the type conversion that matches the comparison — `number` for numeric\ncomparisons, `epochtime` for dates, `string` for text. For example:\n\n```json\n[\n  \"equals\",\n  [\n    \"number\",  // Convert to number before comparison\n    [\n      \"extract\",\n      \"quantity\"\n    ]\n  ],\n  100\n]\n```\n\nExample with datetime conversion:\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",  // Convert to Unix timestamp before comparison\n    [\n      \"extract\",\n      \"createdDate\"\n    ]\n  ],\n  1609459200000  // January 1, 2021 as Unix timestamp in milliseconds\n]\n```\n\nExample with transformations:\n```json\n[\n  \"and\",\n  [\n    \"matches\",\n    [\n      \"lowercase\",  // Convert to lowercase before matching\n      [\n        \"string\",\n        [\n          \"extract\",\n          \"categories\"\n        ]\n      ]\n    ],\n    \"netsuite\"\n  ],\n  [\n    \"notequals\",\n    [\n      \"string\",\n      [\n        \"extract\",\n        \"recurrence.pattern.type\"\n      ]\n    ],\n    \"\"\n  ]\n]\n```\n\nExample comparing a record field with a flow setting:\n```json\n[\n  \"equals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"trantype\"\n    ]\n  ],\n  [\n    \"string\",\n    [\n      \"settings\",\n      \"flow.trantype\"\n    ]\n  ]\n]\n```\n\n**Examples**\n\nExample 1: Status field is not equal to \"cancelled\"\n```json\n[\n  \"notequals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"status\"\n    ]\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"filename\"\n    ]\n  ],\n  \"HC\"\n]\n```\n\nExample 3: Amount is greater than 100\n```json\n[\n  \"greaterthan\",\n  [\n    \"number\",\n    [\n      \"extract\",\n      \"amount\"\n    ]\n  ],\n  100\n]\n```\n\nExample 4: Order date is after January 1, 2023 (UTC). Compare dates as `epochtime`\nagainst epoch milliseconds: a `string` comparison of ISO timestamps orders values with\ndifferent UTC offsets wrongly.\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",\n    [\n      \"extract\",\n      \"orderDate\"\n    ]\n  ],\n  1672531200000\n]\n```\n","items":{"oneOf":[{"title":"String","type":"string"},{"title":"Number","type":"number"},{"title":"Boolean","type":"boolean"},{"title":"Object","type":"object"},{"title":"Array","type":"array"},{"title":"Null","type":"null"}]}}}},"script":{"type":"object","description":"Configuration for programmable script-based filtering. This object enables complex, custom\nfiltering logic beyond what expression-based filtering can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to determine which items\nshould be processed.\n\n**Implementation approach**\n\nScript-based filtering works by:\n1. Executing the specified function from the referenced script\n2. Passing item data to the function\n3. Using the function's return value (true/false) to determine inclusion\n\n**Common use cases**\n\nScript filtering is ideal for:\n- Complex business logic that can't be expressed as simple rules\n- Dynamic filtering criteria that change based on external factors\n- Content-based filtering that requires deep inspection\n- Advanced pattern matching beyond simple wildcards\n- Multi-stage filtering with intermediate logic\n\nFor AI agents: Only use script-based filtering when expression-based filtering is insufficient.\nScript filtering requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to the Script resource that contains the filtering logic. This must be a valid\nObjectId of a Script resource in this account: any other id fails the save with 422\n(\"Only a script owned by the user or a script shared by the Integration App can be used\nat filter.script._scriptId.\").\n\nThe referenced script must contain the function specified in the `function` field\nand must be written to handle filtering specifically. The script receives\nitem data as its input and must return a boolean value indicating whether\nto process the item (true) or skip it (false).\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n"},"function":{"type":"string","description":"Name of the function within the script to execute for filtering decisions. This function\nmust exist in the script referenced by _scriptId.\n\n**Function requirements**\n\nThe specified function must:\n- Accept item data as its first parameter\n- Return a boolean value (true to process the item, false to skip it)\n- Handle errors gracefully\n- Execute efficiently (as it may run for many items)\n\n**Function signature**\n\n```javascript\nfunction filterItems(itemData) {\n  // itemData contains properties of the item being evaluated\n  // Custom logic here\n  return true; // or false to skip the item\n}\n```\n\nFor AI agents: Ensure the function name exactly matches a function defined in the\nreferenced script, as mismatches will cause the filter to fail.\n"}}}}},"Transform":{"type":"object","description":"Configuration for transforming data during processing operations. This object enables\nreshaping of records.\n\n**Transformation capabilities**\n\nCeligo's transformation engine offers powerful features for data manipulation:\n- Precise field mapping with JSONPath expressions\n- Support for any level of nested arrays\n- Formula-based field value generation\n- Dynamic references to flow and integration settings\n\n**Implementation approaches**\n\nThere are two distinct transformation mechanisms available:\n\n**Rule-Based Transformation (`type: \"expression\"`)**\n- **Best For**: Most transformation scenarios from simple to complex\n- **Capabilities**: Field mapping, formula calculations, lookups, nested data handling\n- **Advantages**: Visual configuration, no coding required, intuitive interface\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear mapping requirements or need to reshape data structure\n\n**Script-Based Transformation (`type: \"script\"`)**\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Capabilities**: Full programmatic control, custom processing, complex business rules\n- **Advantages**: Maximum flexibility, can implement any transformation logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Visual transformation tools aren't sufficient for your use case\n","properties":{"type":{"type":"string","description":"Determines which transformation mechanism to use. This choice affects which properties\nmust be configured and how transformation logic is implemented.\n\n**Available types**\n\n**Rule-Based Transformation (`\"expression\"`)**\n- **Required Config**: The `expression` object with mapping definitions\n- **Behavior**: Applies declarative rules to reshape data\n- **Best For**: Most transformation scenarios from simple to complex\n- **Advantages**: Visual configuration, no coding required\n\n**Script-Based Transformation (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to transform data\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard data transformations, use `\"expression\"`\n2. For complex logic or specialized processing, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based transformations. This object enables reshaping data\nwithout requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define transformation rules that\ncan map, modify, and generate data elements.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Field mappings define how input data is transformed to target fields\n- Formulas can be used to calculate or generate new values\n- Lookups can enrich data by fetching related information\n- Mode determines how records are processed (create new or modify existing)\n","properties":{"version":{"type":"string","description":"Version of the expression format. Determines which rules\nproperty contains the transformation logic.\n","enum":["1","2"]},"rules":{"type":"array","description":"Transformation rules for version 1 expressions. An array of\nrule groups; each group is an array of field-mapping objects.\nMost transforms have a single group. Present when `version`\nis `\"1\"`. The output record contains ONLY the generated\nfields — every unmapped field is dropped (v1 has no\nequivalent of Transform 2.0's `modify` mode), and the\nrecord's trace key does not survive the rebuild.\n","items":{"type":"array","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path to read from. Supports multiple\nsyntaxes: bare field names (`id`), dot notation\n(`fulfillment.shipment_id`), slash-prefixed paths\nfor XML (`/FeedProcessingStatus`), wildcards (`*.id`,\n`*.[Internal ID]`), and array indexing (`SDF[0]`).\n"},"generate":{"type":"string","description":"Target field name to write to. Typically a bare name\n(`id`) or dot path (`SDF.Filter.ID`).\n"},"key":{"type":"string","description":"Auto-generated identifier for this rule, used by the\nUI to track individual rules for editing and reordering.\n"}},"required":["extract","generate"]}}},"rulesTwoDotZero":{"type":"object","description":"Configuration for version 2 transformation rules. This object contains the core logic\nfor how data is mapped, enriched, and transformed.\n\n**Capabilities**\n\nTransformation 2.0 provides:\n- Precise field mapping with JSONPath expressions\n- Support for deeply nested data structures\n- Formula-based field generation\n- Dynamic lookups for data enrichment\n- Multiple operating modes to fit different scenarios\n","properties":{"mode":{"type":"string","description":"Transformation mode that determines how records are handled during processing.\n\n**Available modes**\n\n**Create Mode (`\"create\"`)**\n- **Behavior**: Builds entirely new output records from inputs\n- **Use When**: Output structure differs significantly from input\n- **Advantage**: Clean slate approach, no field inheritance\n\n**Modify Mode (`\"modify\"`)**\n- **Behavior**: Makes targeted edits to existing records\n- **Use When**: Output structure should remain similar to input\n- **Advantage**: Preserves unmapped fields from the original record\n","enum":["create","modify"]},"mappings":{"$ref":"#/components/schemas/Mappings"},"lookups":{"allOf":[{"description":"Shared lookup tables used across all mappings defined in the transformation rules.\n\n**Purpose**\n\nLookups provide centralized value translation that can be referenced from any mapping\nin your transformation configuration. They enable consistent translation of codes, IDs,\nand values between systems without duplicating translation logic.\n\n**Usage in transformations**\n\nLookups are particularly valuable in transformations for:\n\n- **Data Normalization**: Standardizing values from diverse source systems\n- **Code Translation**: Converting between different coding systems (e.g., status codes)\n- **Field Enrichment**: Adding descriptive values based on ID or code lookups\n- **Cross-Reference Resolution**: Mapping identifiers between integrated systems\n\n**Implementation**\n\nLookups are defined once in this array and referenced by name in mappings:\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"statusMapping\",\n    \"map\": {\n      \"A\": \"Active\",\n      \"I\": \"Inactive\",\n      \"P\": \"Pending\"\n    },\n    \"default\": \"Unknown Status\"\n  }\n]\n```\n\nThen referenced in mappings using the lookupName property:\n\n```json\n{\n  \"generate\": \"status\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.statusCode\",\n  \"lookupName\": \"statusMapping\"\n}\n```\n\nThe system automatically applies the lookup during transformation processing.\n\nFor complete details on lookup properties and behavior, see the Lookups schema.\n"},{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the JSON shape the transformTwoDotZero processor\nevaluates `mappings[].extract` JSONPath values against at\nflow runtime. Applies only to Transform 2.0 (v2,\n`rulesTwoDotZero`); v1 transforms (the `rules` array on\n`transform.expression.rules`) and script-mode transforms\nignore this field.\n"}}}}},"script":{"type":"object","description":"Configuration for programmable script-based transformations. This object enables complex, custom\ntransformation logic beyond what expression-based transformations can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to transform data according to\nspecialized business rules or complex algorithms.\n\n**Implementation approach**\n\nScript-based transformation works by:\n1. Executing the specified function from the referenced script\n2. Passing input data to the function\n3. Using the function's return value as the transformed output\n\n**Common use cases**\n\nScript transformation is ideal for:\n- Complex business logic that can't be expressed through mappings\n- Algorithmic transformations requiring computation\n- Dynamic transformations based on external factors\n- Legacy system data format compatibility\n- Multi-stage processing with intermediate steps\n\nOnly use script-based transformation when expression-based transformation is insufficient.\nScript transformation requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to a predefined script resource containing the transformation logic.\n\nThe referenced script should contain the function specified in the\n'function' property. It must be a script in this account: any other id fails the save\nwith 422 (\"Only a script owned by the user or a script shared by the Integration App can\nbe used at transform.script._scriptId.\").\n","format":"objectid"},"function":{"type":"string","description":"Name of the function within the script to execute for transformation. This function\nmust exist in the script referenced by _scriptId.\n"}}}}},"Lookups":{"type":"array","description":"Configuration for value-to-value transformations using lookup tables.\n\n**Purpose**\n\nLookups provide a way to translate values from one system to another. They transform\ninput values into output values using either static mapping tables or\ndynamic lookup caches.\n\n**Lookup mechanisms**\n\nThere are two distinct lookup mechanisms available:\n\n1. **Static Lookups**: Define a simple key-value map object and store it as part of your resource\n   - Best for: Small, fixed sets of values that rarely change\n   - Implementation: Configure the `map` object with input-to-output value mappings\n   - Example: Country codes, status values, simple translations\n\n2. **Dynamic Lookups**: Reference an existing 'Lookup Cache' resource in your Celigo account\n   - Best for: Large datasets, frequently changing values, or complex reference data\n   - Implementation: Configure `_lookupCacheId` to reference cached data maintained independently\n   - Example: Product catalogs, customer databases, pricing information\n\n**Property usage**\n\nThere are two mutually exclusive ways to configure lookups, depending on which mechanism you choose:\n\n1. **For Static Mappings**: Configure the `map` property with a direct key-value object\n   ```json\n   \"map\": {\"US\": \"United States\", \"CA\": \"Canada\"}\n   ```\n\n2. **For Dynamic Lookups**: Configure the following properties:\n   - `_lookupCacheId`: Reference to the lookup cache resource\n   - `extract`: JSON path to extract specific value from the returned lookup object\n\n**When to use**\n\nLookups are ideal for:\n\n1. **Value Translation**: Mapping codes or IDs to human-readable values\n\n2. **Data Enrichment**: Adding related information to records during processing\n\n3. **Normalization**: Ensuring consistent formatting of values across systems\n\n**Implementation details**\n\nLookups can be referenced in:\n\n1. **Field Mappings**: Direct use in field transformation configurations\n\n2. **Handlebars Templates**: Use within templates with the syntax:\n   ```\n   {{lookup 'lookupName' record.fieldName}}\n   ```\n\n**Example usage**\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"countryCodeToName\",\n    \"map\": {\n      \"US\": \"United States\",\n      \"CA\": \"Canada\",\n      \"UK\": \"United Kingdom\"\n    },\n    \"default\": \"Unknown Country\",\n    \"allowFailures\": true\n  },\n  {\n    \"name\": \"productDetails\",\n    \"_lookupCacheId\": \"60a2c4e6f321d800129a1a3c\",\n    \"extract\": \"$.details.price\",\n    \"allowFailures\": false\n  }\n]\n```\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for the lookup table within this configuration.\n\nThis name must be unique within the scope where the lookup is defined and is used to reference\nthe lookup in handlebars templates with the syntax {{lookup 'name' value}}.\n\nChoose descriptive names that indicate the transformation purpose, such as:\n- \"countryCodeToName\" for country code to full name conversion\n- \"statusMapping\" for status code translations\n- \"departmentCodes\" for department code to name mapping\n"},"map":{"type":["object","null"],"description":"The lookup mapping table as key-value pairs. The platform stores `null`\nhere on dynamic lookups, which resolve values at runtime instead of\nfrom a static table.\n\nThis object contains the input values as keys and their corresponding\noutput values. When a input value matches a key in this object,\nit will be replaced with the corresponding value.\n\nThe map should be kept to a reasonable size (typically under 100 entries)\nfor optimal performance. For larger mapping requirements, consider using\ndynamic lookups instead.\n\nMaps can include:\n- Simple code to name conversions: {\"US\": \"United States\"}\n- Status transformations: {\"A\": \"Active\", \"I\": \"Inactive\"}\n- ID to name mappings: {\"100\": \"Marketing\", \"200\": \"Sales\"}\n\nValues can be strings, numbers, or booleans, but all are stored as strings\nin the configuration.\n"},"_lookupCacheId":{"type":"string","description":"Reference to a LookupCache resource that contains the reference data for the lookup.\n\n**Purpose**\n\nThis field connects the lookup to an external data source that has been cached in the system.\nUnlike static lookups that use the `map` property, dynamic lookups can reference large datasets\nor frequently changing information without requiring constant updates to the integration.\n\n**Implementation details**\n\nThe LookupCache resource referenced by this ID contains:\n- The data records to be used as a reference source\n- Configuration for how the data should be indexed and accessed\n- Caching parameters to balance performance with data freshness\n\n**Usage patterns**\n\nCommonly used to reference:\n- Product catalogs or SKU databases\n- Customer or account information\n- Pricing tables or discount rules\n- Complex business logic lookup tables\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n","format":"objectid"},"extract":{"type":"string","description":"JSON path expression that extracts a specific value from the cached lookup object.\n\n**Purpose**\n\nWhen using dynamic lookups with a LookupCache, this JSON path identifies which field to extract\nfrom the cached object after it has been retrieved using the lookup key.\n\n**Implementation details**\n\n- Must use JSON path syntax (similar to mapping extract fields)\n- Operates on the cached object returned by the lookup operation\n- Examples:\n  - \"$.name\" - Extract the name field from the top level\n  - \"$.details.price\" - Extract a nested price field\n  - \"$.attributes[0].value\" - Extract a value from the first element of an array\n\n**Usage scenario**\n\nWhen a lookup cache contains complex objects:\n```json\n// Cache entry for key \"PROD-123\":\n{\n  \"id\": \"PROD-123\",\n  \"name\": \"Premium Widget\",\n  \"details\": {\n    \"price\": 99.99,\n    \"currency\": \"USD\",\n    \"inStock\": true\n  }\n}\n```\n\nSetting extract to \"$.details.price\" would return 99.99 as the lookup result.\n\nIf no extract is provided, the entire cached object is returned as the lookup result.\n"},"default":{"type":["string","null"],"description":"Default value to use when the source value is not found in the lookup map.\nThe platform stores `null` here when no default is configured.\n\nThis value is used as a fallback when:\n1. The source value doesn't match any key in the map\n2. allowFailures is true or omitted\n\nSetting an appropriate default helps prevent flow failures due to unexpected\nvalues and provides predictable behavior for edge cases.\n\nCommon default patterns include:\n- Descriptive unknowns: \"Unknown Country\", \"Unspecified Status\"\n- Original value indicators: \"{Original Value}\", \"No mapping found\"\n- Neutral values: \"Other\", \"N/A\", \"Miscellaneous\"\n\nA save refuses a default together with an explicit `allowFailures: false`\n(422 `Lookups with default set can not have allowFailures explicitly set to\nfalse.`): a lookup either fails a miss (`allowFailures: false`, no default) or\nfalls back to its default.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, missing lookup values will use the default value rather than causing an error.\n\n**Behavior control**\n\nThis field determines how the system handles source values that don't exist in the map:\n\n- true: Use the default value for missing mappings and continue processing\n- false: Treat missing mappings as errors, failing the record (`lookup_error` for a\n  static map, `lookupcache_evaluation_failed` for a lookup cache)\n- omitted or null: the record doesn't fail either. On an import a static lookup writes\n  its default if it has one, and a static or cache lookup without one leaves the\n  mapped field out of the output, with no error\n\n**Recommendation**\n\nSet this to true when:\n- New source values might appear over time\n- Data quality issues could introduce unexpected values\n- Processing should continue even with imperfect mapping\n\nSet this to false when:\n- Complete data accuracy is critical\n- All possible source values are known and controlled\n- Missing mappings indicate serious data problems that should be addressed\n\nA default raises no error: records that miss go out with the default value and\nnothing reports them. Use true only when that fallback is acceptable at the\ndestination, and set false explicitly when a missing value must stop the record.\n"}}}},"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: one in the script named by `_scriptId`, or one the code deployed on\nthe stack named by `_stackId` exposes."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`. Send this or `_stackId`, not both.\nMust be a script in this account: any other id fails the save with 422, naming the hook,\nsuch as \"Only a script owned by the user or a script shared by the Integration App can\nbe used at hooks.preMap._scriptId.\" on an import's `preMap`."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments.\nThe API doesn't enforce the either/or: a save carrying both `_scriptId` and `_stackId`\nanswers 200 and stores both, and the script and the stack then each list the step as a\ndependent."},"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."}}},"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"},"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"]},"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"}}}}}}
````

## List AI agents

> Returns all imports in the account. The response includes every import type,\
> not just AI agents — filter client-side by \`adaptorType: "AiAgentImport"\`.

````json
{"openapi":"3.2.0","info":{"title":"AI Agents","version":"1.0.0"},"tags":[{"name":"AI Agents","description":"AI agents are LLM-powered import steps that classify, extract, validate, or\ngenerate data within integration flows. They are stored as import resources\nwith `adaptorType: AiAgentImport` and managed via the `/v1/imports` endpoints.\n\nThree built-in providers — OpenAI (via the Responses API), Google Gemini (via\nLiteLLM), and Anthropic Claude (via LiteLLM) — run on platform-managed\ncredentials or, with a `_connectionId`, on your own key (BYOK). Catalog\nproviders — Mistral, xAI, Hugging Face, DeepSeek, Cohere, and Groq — use one generic\nflat configuration format and always run on a BYOK connection.\n\n## AI agent schema\n\n{% openapi-schemas spec=\"ai-agent\" schemas=\"AiAgent\" 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"}},"After":{"name":"after","in":"query","required":false,"description":"Opaque cursor for forward pagination. Pass the value from the `Link`\nresponse header (`rel=\"next\"`) to fetch the next page.","schema":{"type":"string"}}},"schemas":{"AiAgent":{"type":"object","required":["_id","name","adaptorType","aiAgent","createdAt","lastModified"],"description":"AI agent import object as returned by the API.","allOf":[{"$ref":"#/components/schemas/AiAgentBase"},{"$ref":"#/components/schemas/ResourceResponse"},{"type":"object","properties":{"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Export that feeds data into this import within a flow."},"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"apiIdentifier":{"type":"string","readOnly":true,"description":"Ten-character hex identifier for the resource."},"lookups":{"type":"array","readOnly":true,"description":"Lookup configurations associated with this import.","items":{"type":"object"}},"_templateId":{"type":"string","format":"objectId","readOnly":true,"description":"Template this AI agent was created from."},"draftExpiresAt":{"type":"string","format":"date-time","readOnly":true,"description":"When the draft version of this AI agent expires. Drafts are staged by Celigo's\nAI assistant when it proposes changes for review; this field is present only\nwhile an unapproved draft is pending."},"debugUntil":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp until which verbose debug logging runs for this AI agent. Read-only\non create/update; enable it through the debug PATCH endpoint, not a full update."}}}]},"AiAgentBase":{"type":"object","description":"Writable AI agent import fields shared by the request and response schemas. Set\n`aiAgent.provider` to choose the LLM backend, then configure the matching format:\n`aiAgent.openai` for OpenAI, `aiAgent.litellm` for Gemini and Anthropic Claude\n(with provider-specific settings under `aiAgent.litellm._overrides`), or the\ngeneric flat format (`aiAgent.model`, `aiAgent.modelOptions`, …) for the catalog\nproviders Mistral, xAI, Hugging Face, DeepSeek, Cohere, and Groq.","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Display name for the AI agent."},"description":{"type":"string","description":"Optional description of the AI agent's purpose."},"_connectionId":{"type":"string","format":"objectId","description":"Connection for BYOK (bring your own key) — an HTTP connection to the vendor's API.\nOptional for the built-in providers (`openai`, `gemini`, `anthropic`), which run on\nplatform-managed credentials when it is omitted. Catalog providers (`mistral`, `xai`,\n`huggingface`, `deepseek`, `cohere`, `groq`) have no platform-managed credentials: the save\nrequires it — without one the agent fails to save with 422 `connection_required` on\n`_connectionId`."},"_integrationId":{"type":"string","format":"objectId","description":"Integration this AI agent belongs to. Omitted for a standalone agent."},"adaptorType":{"type":"string","enum":["AiAgentImport"],"description":"Must be `AiAgentImport`."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this AI agent was created from, set when it is part of an installed integration app."},"aiAgent":{"$ref":"#/components/schemas/AiAgentConfig"},"mappings":{"allOf":[{"$ref":"#/components/schemas/Mappings"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Selects the data context that `mappings[].extract` evaluates against. With no\nmappings the agent receives the entire record as JSON (or only the record's own\ntop-level `text`, `record` or `files` fields, when it has any) and `inputContext`\nis moot. `record`\n(the default) extracts against the flat record; `envelope` extracts against the\nruntime envelope, which exposes the record alongside job, settings, and other\nrun-time metadata (`$.record.field`, `$.settings.field`, etc.). Same contract as\nthe import resource's `inputContext`."},"filter":{"description":"Filter applied to incoming records before they are sent to the LLM. Records that match\ncontinue through the agent; records that don't skip it, counted as ignored with no model\ncall, and continue to the next step.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"responseTransform":{"description":"Transformation that reshapes the LLM's response after records are processed, before the\nresponse is handled by response mappings and downstream steps.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the agent's import lifecycle.\nAI agents do not aggregate records, so the `postAggregate` hook that other import\ntypes expose is intentionally omitted here.","properties":{"preMap":{"description":"Hook that runs on each page of records before the agent's mappings are applied.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postMap":{"description":"Hook that runs after mappings are applied, before records are sent to the LLM.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postSubmit":{"description":"Hook that runs after the LLM responds, with both the submitted records and the responses.","allOf":[{"$ref":"#/components/schemas/Hook"}]}}},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"distributed":{"type":"boolean","description":"Always false for AI agent imports — the agent executes on Celigo's servers, not inside a target application."},"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 are silently skipped. Rarely meaningful for AI agents; accepted for import-surface parity."},"ignoreMissing":{"type":"boolean","description":"When true, records that do not already exist in the destination are silently skipped. Rarely meaningful for AI agents; accepted for import-surface parity."},"mockResponse":{"type":"array","description":"Predefined response records used in place of calling the LLM when testing the agent,\nso flows can run without spending tokens. Records must be in integrator.io canonical\nformat — 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 for structured-output agents."},"_text":{"type":"string","description":"Mock response body for text-output agents."},"_headers":{"type":"object","description":"Mock response headers."},"blobKey":{"type":"string","description":"Mock output of an agent with `blob` output (image generation:\n`openai.output.format.type` or `litellm.responseFormat.type` set to `blob`),\nwhich returns the stored image's key here. Unrelated to the import-level `blob`\nflag, which agents refuse."}}}}}},"AiAgentConfig":{"type":"object","description":"AI agent configuration for an AiAgentImport. An `ai_agent` guardrail is configured by\nthe guardrail spec's `GuardrailAiAgentConfig` instead, which has no `tools` arrays.\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 the\nsave requires one — without it the agent fails to save with 422\n`connection_required` on `_connectionId`.\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","max"],"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":"Caps the length of the model's response, in tokens. An agent created without it is\nsaved with 5000; raise it for long outputs such as large extractions."},"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":"Which editor the UI opens `jsonSchema` in; the schema sent to the provider\nis the same either way, and the field is only meaningful when `type` is\n\"json_schema\". Unset opens the visual editor, and an edit there rebuilds\n`jsonSchema` from the field list, keeping only `type`, `properties`,\n`required`, `items`, `description`, `enum`, `pattern`, length and numeric\nbounds, `multipleOf`, `minItems`/`maxItems` and a `\"null\"` type — other\nkeywords are dropped without a prompt, and `integer` becomes `number`."},"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`."}}},"schema":{"type":"object","readOnly":true,"additionalProperties":true,"description":"Server-written copy of `jsonSchema`, returned whenever `jsonSchema` is set — on\nAI agent imports and on guardrail agents alike. Send `jsonSchema`, not this field."}},"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","code_execution","tool"],"description":"Type of tool. Every type except `code_execution` is configured through the\nsibling object of the same name; OpenAI spells this one `code_execution`, while\nthe Gemini and Anthropic overrides spell theirs `codeExecution` — the other spelling\nfails with 422 `enum`."},"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"]}}}},"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":"Which editor the UI opens `jsonSchema` in; the schema sent to the provider is\nthe same either way, and the field is only meaningful when `type` is\n\"json_schema\". Unset opens the visual editor, and an edit there rebuilds\n`jsonSchema` from the field list, keeping only `type`, `properties`, `required`,\n`items`, `description`, `enum`, `pattern`, length and numeric bounds,\n`multipleOf`, `minItems`/`maxItems` and a `\"null\"` type — other keywords are\ndropped without a prompt, and `integer` becomes `number`."},"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","codeExecution","mcp","tool"],"description":"Type of Gemini tool. `codeExecution` is the Gemini spelling — `code_execution`\nfails with 422 `enum`."},"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"]}}}},"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","codeExecution"],"description":"Type of Claude tool. `codeExecution` is the Anthropic spelling — `code_execution`\nfails with 422 `enum`."},"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). Each\nentry is either a plain tool name or an object carrying display\nmetadata — same contract as the OpenAI and Gemini `allowedTools`.","items":{"oneOf":[{"title":"name","type":"string","description":"Tool name as exposed by the MCP server."},{"title":"tool","type":"object","required":["name"],"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."}}}]}}}},"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."}}}}}}}}},"Mappings":{"type":"array","description":"Array of field mapping configurations for transforming data from one format into another.\n\n**Guidance**\n\nThis schema is designed around RECURSION as its core architectural principle. Understanding this recursive\nnature is essential for building effective mappings:\n\n1. The schema is self-referential by design - a mapping can contain nested mappings of the same structure\n2. Complex data structures (nested objects and arrays of objects, to any depth) are ALL\n   handled through this recursive pattern; an array of arrays is not (build one in a script hook)\n3. Each mapping handles one level of the data structure; deeper levels are handled by nested mappings\n\nWhen generating mappings programmatically:\n- For simple fields (string, number, boolean): Create single mapping objects\n- For objects: Create a parent mapping with nested 'mappings' array containing child field mappings\n- For arrays: Use 'buildArrayHelper' with extract paths defining array inputs and\n  recursive 'mappings' to define object structures\n\nThe system will process these nested structures recursively during runtime, ensuring proper construction\nof complex hierarchical data while maintaining excellent performance.\n\nOn save, a leaf row with neither a source (`extract`, `hardCodedValue`, `lookupName`) nor a\ndestination (`generate`) is dropped silently, at every nesting level, and so is an object or\nobject-array row left with no source, no destination and no remaining child `mappings` or\n`buildArrayHelper` entries; a row with only one of the two, and a source-less, destination-less\nwrapper that still has children, are kept.\n\nThe shape of the array is the mapper's **JSON structure**. Named top-level items produce an object\nrecord (`record { }`). Exactly one top-level item with `dataType: objectarray` and no `generate` is\nthe `rows [ ]` structure: the mapped record is an array — usually one element per element of the\nsource array that root's `buildArrayHelper` iterates — Handlebars templates such as `http.body`\nbind it as `rows` instead of `record`, and the mapping editor shows that row's destination disabled.\n","items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}}},"items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}},"Filter":{"type":"object","description":"Configuration for selectively processing records based on specified criteria. This object enables\nprecise control over which items are included or excluded from processing operations.\n\n**Filter behavior**\n\nWhen configured, the filter is applied before processing begins:\n- Items that match the filter criteria are processed\n- Items that don't match are completely skipped\n- No partial processing is performed\n\nSend the whole object. A rules array sent alone as the `filter` value is not\nstored, and a full update of an export that carries one removes the filter the\nexport had, with no error.\n\n**Implementation approaches**\n\nThere are two distinct filtering mechanisms available:\n\n**Rule-Based Filtering (`type: \"expression\"`)**\n- **Best For**: Common filtering patterns based on standard attributes\n- **Capabilities**: Filter by names, values, dates, numerical ranges, text patterns\n- **Advantages**: Declarative, no coding required, consistent performance\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear, static criteria for selection\n\n**Script-Based Filtering (`type: \"script\"`)**\n- **Best For**: Complex logic, dynamic criteria, or business rules\n- **Capabilities**: Full programmatic control, access to complete metadata\n- **Advantages**: Maximum flexibility, can implement any filtering logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Simple rules aren't sufficient or logic needs to be dynamic\n","properties":{"type":{"type":"string","description":"Determines which filtering mechanism to use. This choice affects which properties\nmust be configured and how filtering logic is implemented.\n\n**Available types**\n\n**Rule-Based Filtering (`\"expression\"`)**\n- **Required Config**: The `expression` object with rule definitions\n- **Behavior**: Evaluates declarative rules against item attributes\n- **Best For**: Common patterns like name matching, date ranges, value limits\n- **Advantages**: Simpler to configure, no custom code required\n\n**Script-Based Filtering (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to determine which items to process\n- **Best For**: Complex conditions, business logic, dynamic criteria\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard filtering needs (name, size, date), use `\"expression\"`\n2. For complex logic or conditions not covered by expressions, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based filtering. This object enables filtering\nitems based on common attributes without requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define filtering rules that\ncan match against item attributes like name, type, value, date, and other properties.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Rules can be combined with AND/OR logic\n- Each rule can check a specific attribute\n- Multiple conditions can be applied (ranges, pattern matching, exact matches)\n\n**Common filter patterns**\n\n1. **Pattern matching**: Regular expressions with `matches`\n2. **Value range filtering**: Numbers between min and max values\n3. **Date range filtering**: Items created/modified within specific time ranges\n4. **Status checking**: Items with specific status values or properties\n\nFor AI agents: Rule-based filtering should be your first choice when the filtering criteria\ncan be expressed in terms of standard attributes. Only use script-based filtering when\nmore complex logic is required.\n","properties":{"version":{"type":"string","description":"Version identifier for the expression format. Currently only version \"1\" is supported.\n\nThis field ensures future compatibility if the expression format evolves. Always set to \"1\"\nfor current implementations.\n","enum":["1"]},"rules":{"type":"array","description":"Expression array defining filter conditions using prefix notation. The first element is the operator,\nfollowed by its operands which may themselves be nested expression arrays.\n\nThe rule expression follows this pattern:\n- First element: Operator name (string)\n- Remaining elements: Operands for that operator (values or nested expressions)\n\n**Expression structure**\n\nFilter expressions use a prefix notation where operators appear before their operands:\n```\n[operator, operand1, operand2, ...]\n```\n\n**Comparison Operators**\n- `\"equals\"`: Exact match (equals)\n- `\"notequals\"`: Not equal to value (not equals)\n- `\"greaterthan\"`: Value is greater than specified value (is greater than)\n- `\"greaterthanequals\"`: Value is greater than or equal to specified value (is greater than or equals)\n- `\"lessthan\"`: Value is less than specified value (is less than)\n- `\"lessthanequals\"`: Value is less than or equal to specified value (is less than or equals)\n- `\"startswith\"`: String starts with specified prefix (starts with)\n- `\"notstartswith\"`: String does not start with specified prefix (does not start with)\n- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"notendswith\"`: String does not end with specified suffix (does not end with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"empty\"`: Field is missing or empty (is empty)\n- `\"notempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches a regular expression, ignoring case (matches)\n\nOperator names are case-insensitive: `NOTEMPTY` and `endsWith` work and are stored\nas sent. `isempty`, `isnotempty`, `anyof`, `notcontains`, `doesnotstartwith`,\n`doesnotendwith`, `notmatches`, `doesnotmatch` and `notequal` are not operators:\nthey are refused with 422 \"Invalid operation: <name>\", with the name in lowercase,\nboth when the filter is saved and by `POST /v1/processors/filter`. To negate a\ncondition, wrap it in `not`.\n\nThe string comparisons (`equals`, `contains`, `startswith`, `endswith` and their\nnegations) are case-sensitive; wrap the field in `lowercase` to compare without case.\n`matches` is not: every pattern is compiled with the flags `gi`, so `^paid$` also\nmatches `PAID` and `[a-z]` matches capital letters. An inline flag such as `(?i)`\nmakes the pattern invalid. The filter still saves, but in a run every record fails\nwith `FILTER_EVALUATION_FAILED` (\"Invalid regular expression: /(?i)…/gi: Invalid\ngroup\"), and `POST /v1/processors/filter` refuses the call with 422 (live-verified\n2026-10-22).\n\n**Logical Operators**\n- `\"and\"`: All conditions must be true\n- `\"or\"`: At least one condition must be true\n- `\"not\"`: Negates the condition\n\n**Field Access and Type Conversion**\n- `\"extract\"`: Access a field from the item by name\n- `\"settings\"`: Access a custom setting from the flow, flow step, or integration configuration\n- `\"boolean\"`: Convert value to Boolean type\n- `\"epochtime\"`: Convert value to Epoch Time (Unix timestamp)\n- `\"number\"`: Convert value to Number type\n- `\"string\"`: Convert value to String type\n\n**Field Access Details**\n\n**Using `extract` to access record fields:**\n- Retrieves values from the current record being processed\n- Can access nested properties using dot notation (e.g., `\"customer.email\"`)\n- Paths are relative to the record itself: `[\"extract\", \"record.status\"]`\n  matches nothing\n- In a record filter, must be wrapped in a type conversion (`string`, `number`,\n  `boolean` or `epochtime`): a bare `[\"extract\", ...]` operand is refused with 422\n  \"Field operation extract must be called by a Type Conversion Operation but was called\n  by <operator>\", both when an import's filter is saved and by\n  `POST /v1/processors/filter`\n\n**Using `settings` to access configuration values:**\n- Retrieves values from the integration's configuration settings\n- Supports different scopes with prefix notation:\n  - `flow.settingName`: Access flow-level settings\n  - `flowGrouping.settingName`: Access the flow group's settings\n  - `export.settingName`: Access the export's (or lookup's) own settings\n  - `import.settingName`: Access the import's own settings\n  - `integration.settingName`: Access integration-level settings\n  - `connection.settingName`, `iClient.settingName`: Access the step's\n    connection and iClient settings\n- A scope whose resource has no settings can be absent; a rule that reads\n  it matches nothing\n- Must be wrapped in a type conversion like `extract`: a bare\n  `[\"settings\", ...]` operand is refused with 422 \"Field operation settings\n  must be called by a Type Conversion Operation\"\n- Useful for dynamic filtering based on configuration\n\n**Field Transformations**\n- `\"lowercase\"`: Convert string to lowercase\n- `\"uppercase\"`: Convert string to uppercase\n- `\"ceiling\"`: Round number up to the nearest integer\n- `\"floor\"`: Round number down to the nearest integer\n- `\"abs\"`: Get absolute value of a number\n\n**Arithmetic** (number operands)\n- `\"add\"`, `\"subtract\"`, `\"multiply\"`, `\"divide\"`, `\"modulo\"`: for example\n  `[\"subtract\", [\"number\", [\"extract\", \"total\"]], [\"number\", [\"extract\", \"discount\"]]]`.\n  `mod` is not an operator (422 \"Invalid operation: mod.\").\n\n**Dates**\n- `epochtime` reads ISO 8601 strings and epoch-millisecond numbers. An empty\n  string, a string that isn't a date, or epoch milliseconds sent as a string\n  fail it with \"Invalid date. Cannot convert given string or number to date\n  time.\", followed by `; record=` and the record as JSON: in a run that record fails (`FILTER_EVALUATION_FAILED`, source\n  `output_filter` on an export), and `POST /v1/processors/filter` and an\n  export preview refuse the whole call with 422. `and` and `or` evaluate\n  every operand, so a `notempty` test beside it doesn't prevent the failure.\n  A missing or null field doesn't match.\n- `datetime`, `date` and `timestamp` are not type conversions: 422\n  \"Invalid operation: <name>.\"\n- `number` reads a missing or empty field as 0.\n\n**Handlebars operands**\n- An operand can also be a string holding exactly one Handlebars expression, such as\n  `\"{{record.status}}\"` or `\"{{lowercase record.code}}\"`, on either side of the\n  operator. It renders against the record (`record.<field>`; an unprefixed\n  `{{status}}` renders empty) before the comparison.\n- The rendered value is a string: wrap it in `[\"number\", ...]` to compare it with a\n  number, or the rule is refused with 422.\n- `epochtime` does not render Handlebars: `[\"epochtime\", \"{{timestamp}}\"]` is refused\n  with 422 \"invalid datetime format\".\n- A string with text around the expression, or with more than one expression, is\n  compared as a literal.\n- `[\"number\", \"{{timestamp \\\"x\\\"}}\"]` is the current time in epoch milliseconds,\n  so `[\"greaterthanequals\", [\"epochtime\", [\"extract\", \"created\"]], [\"subtract\",\n  [\"number\", \"{{timestamp \\\"x\\\"}}\"], 604800000]]` keeps records created in the\n  last 7 days.\n- An unknown helper fails the rule with 422 \"Failed to evaluate handlebars\n  expression\".\n\nChoose the type conversion that matches the comparison — `number` for numeric\ncomparisons, `epochtime` for dates, `string` for text. For example:\n\n```json\n[\n  \"equals\",\n  [\n    \"number\",  // Convert to number before comparison\n    [\n      \"extract\",\n      \"quantity\"\n    ]\n  ],\n  100\n]\n```\n\nExample with datetime conversion:\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",  // Convert to Unix timestamp before comparison\n    [\n      \"extract\",\n      \"createdDate\"\n    ]\n  ],\n  1609459200000  // January 1, 2021 as Unix timestamp in milliseconds\n]\n```\n\nExample with transformations:\n```json\n[\n  \"and\",\n  [\n    \"matches\",\n    [\n      \"lowercase\",  // Convert to lowercase before matching\n      [\n        \"string\",\n        [\n          \"extract\",\n          \"categories\"\n        ]\n      ]\n    ],\n    \"netsuite\"\n  ],\n  [\n    \"notequals\",\n    [\n      \"string\",\n      [\n        \"extract\",\n        \"recurrence.pattern.type\"\n      ]\n    ],\n    \"\"\n  ]\n]\n```\n\nExample comparing a record field with a flow setting:\n```json\n[\n  \"equals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"trantype\"\n    ]\n  ],\n  [\n    \"string\",\n    [\n      \"settings\",\n      \"flow.trantype\"\n    ]\n  ]\n]\n```\n\n**Examples**\n\nExample 1: Status field is not equal to \"cancelled\"\n```json\n[\n  \"notequals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"status\"\n    ]\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"filename\"\n    ]\n  ],\n  \"HC\"\n]\n```\n\nExample 3: Amount is greater than 100\n```json\n[\n  \"greaterthan\",\n  [\n    \"number\",\n    [\n      \"extract\",\n      \"amount\"\n    ]\n  ],\n  100\n]\n```\n\nExample 4: Order date is after January 1, 2023 (UTC). Compare dates as `epochtime`\nagainst epoch milliseconds: a `string` comparison of ISO timestamps orders values with\ndifferent UTC offsets wrongly.\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",\n    [\n      \"extract\",\n      \"orderDate\"\n    ]\n  ],\n  1672531200000\n]\n```\n","items":{"oneOf":[{"title":"String","type":"string"},{"title":"Number","type":"number"},{"title":"Boolean","type":"boolean"},{"title":"Object","type":"object"},{"title":"Array","type":"array"},{"title":"Null","type":"null"}]}}}},"script":{"type":"object","description":"Configuration for programmable script-based filtering. This object enables complex, custom\nfiltering logic beyond what expression-based filtering can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to determine which items\nshould be processed.\n\n**Implementation approach**\n\nScript-based filtering works by:\n1. Executing the specified function from the referenced script\n2. Passing item data to the function\n3. Using the function's return value (true/false) to determine inclusion\n\n**Common use cases**\n\nScript filtering is ideal for:\n- Complex business logic that can't be expressed as simple rules\n- Dynamic filtering criteria that change based on external factors\n- Content-based filtering that requires deep inspection\n- Advanced pattern matching beyond simple wildcards\n- Multi-stage filtering with intermediate logic\n\nFor AI agents: Only use script-based filtering when expression-based filtering is insufficient.\nScript filtering requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to the Script resource that contains the filtering logic. This must be a valid\nObjectId of a Script resource in this account: any other id fails the save with 422\n(\"Only a script owned by the user or a script shared by the Integration App can be used\nat filter.script._scriptId.\").\n\nThe referenced script must contain the function specified in the `function` field\nand must be written to handle filtering specifically. The script receives\nitem data as its input and must return a boolean value indicating whether\nto process the item (true) or skip it (false).\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n"},"function":{"type":"string","description":"Name of the function within the script to execute for filtering decisions. This function\nmust exist in the script referenced by _scriptId.\n\n**Function requirements**\n\nThe specified function must:\n- Accept item data as its first parameter\n- Return a boolean value (true to process the item, false to skip it)\n- Handle errors gracefully\n- Execute efficiently (as it may run for many items)\n\n**Function signature**\n\n```javascript\nfunction filterItems(itemData) {\n  // itemData contains properties of the item being evaluated\n  // Custom logic here\n  return true; // or false to skip the item\n}\n```\n\nFor AI agents: Ensure the function name exactly matches a function defined in the\nreferenced script, as mismatches will cause the filter to fail.\n"}}}}},"Transform":{"type":"object","description":"Configuration for transforming data during processing operations. This object enables\nreshaping of records.\n\n**Transformation capabilities**\n\nCeligo's transformation engine offers powerful features for data manipulation:\n- Precise field mapping with JSONPath expressions\n- Support for any level of nested arrays\n- Formula-based field value generation\n- Dynamic references to flow and integration settings\n\n**Implementation approaches**\n\nThere are two distinct transformation mechanisms available:\n\n**Rule-Based Transformation (`type: \"expression\"`)**\n- **Best For**: Most transformation scenarios from simple to complex\n- **Capabilities**: Field mapping, formula calculations, lookups, nested data handling\n- **Advantages**: Visual configuration, no coding required, intuitive interface\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear mapping requirements or need to reshape data structure\n\n**Script-Based Transformation (`type: \"script\"`)**\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Capabilities**: Full programmatic control, custom processing, complex business rules\n- **Advantages**: Maximum flexibility, can implement any transformation logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Visual transformation tools aren't sufficient for your use case\n","properties":{"type":{"type":"string","description":"Determines which transformation mechanism to use. This choice affects which properties\nmust be configured and how transformation logic is implemented.\n\n**Available types**\n\n**Rule-Based Transformation (`\"expression\"`)**\n- **Required Config**: The `expression` object with mapping definitions\n- **Behavior**: Applies declarative rules to reshape data\n- **Best For**: Most transformation scenarios from simple to complex\n- **Advantages**: Visual configuration, no coding required\n\n**Script-Based Transformation (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to transform data\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard data transformations, use `\"expression\"`\n2. For complex logic or specialized processing, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based transformations. This object enables reshaping data\nwithout requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define transformation rules that\ncan map, modify, and generate data elements.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Field mappings define how input data is transformed to target fields\n- Formulas can be used to calculate or generate new values\n- Lookups can enrich data by fetching related information\n- Mode determines how records are processed (create new or modify existing)\n","properties":{"version":{"type":"string","description":"Version of the expression format. Determines which rules\nproperty contains the transformation logic.\n","enum":["1","2"]},"rules":{"type":"array","description":"Transformation rules for version 1 expressions. An array of\nrule groups; each group is an array of field-mapping objects.\nMost transforms have a single group. Present when `version`\nis `\"1\"`. The output record contains ONLY the generated\nfields — every unmapped field is dropped (v1 has no\nequivalent of Transform 2.0's `modify` mode), and the\nrecord's trace key does not survive the rebuild.\n","items":{"type":"array","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path to read from. Supports multiple\nsyntaxes: bare field names (`id`), dot notation\n(`fulfillment.shipment_id`), slash-prefixed paths\nfor XML (`/FeedProcessingStatus`), wildcards (`*.id`,\n`*.[Internal ID]`), and array indexing (`SDF[0]`).\n"},"generate":{"type":"string","description":"Target field name to write to. Typically a bare name\n(`id`) or dot path (`SDF.Filter.ID`).\n"},"key":{"type":"string","description":"Auto-generated identifier for this rule, used by the\nUI to track individual rules for editing and reordering.\n"}},"required":["extract","generate"]}}},"rulesTwoDotZero":{"type":"object","description":"Configuration for version 2 transformation rules. This object contains the core logic\nfor how data is mapped, enriched, and transformed.\n\n**Capabilities**\n\nTransformation 2.0 provides:\n- Precise field mapping with JSONPath expressions\n- Support for deeply nested data structures\n- Formula-based field generation\n- Dynamic lookups for data enrichment\n- Multiple operating modes to fit different scenarios\n","properties":{"mode":{"type":"string","description":"Transformation mode that determines how records are handled during processing.\n\n**Available modes**\n\n**Create Mode (`\"create\"`)**\n- **Behavior**: Builds entirely new output records from inputs\n- **Use When**: Output structure differs significantly from input\n- **Advantage**: Clean slate approach, no field inheritance\n\n**Modify Mode (`\"modify\"`)**\n- **Behavior**: Makes targeted edits to existing records\n- **Use When**: Output structure should remain similar to input\n- **Advantage**: Preserves unmapped fields from the original record\n","enum":["create","modify"]},"mappings":{"$ref":"#/components/schemas/Mappings"},"lookups":{"allOf":[{"description":"Shared lookup tables used across all mappings defined in the transformation rules.\n\n**Purpose**\n\nLookups provide centralized value translation that can be referenced from any mapping\nin your transformation configuration. They enable consistent translation of codes, IDs,\nand values between systems without duplicating translation logic.\n\n**Usage in transformations**\n\nLookups are particularly valuable in transformations for:\n\n- **Data Normalization**: Standardizing values from diverse source systems\n- **Code Translation**: Converting between different coding systems (e.g., status codes)\n- **Field Enrichment**: Adding descriptive values based on ID or code lookups\n- **Cross-Reference Resolution**: Mapping identifiers between integrated systems\n\n**Implementation**\n\nLookups are defined once in this array and referenced by name in mappings:\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"statusMapping\",\n    \"map\": {\n      \"A\": \"Active\",\n      \"I\": \"Inactive\",\n      \"P\": \"Pending\"\n    },\n    \"default\": \"Unknown Status\"\n  }\n]\n```\n\nThen referenced in mappings using the lookupName property:\n\n```json\n{\n  \"generate\": \"status\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.statusCode\",\n  \"lookupName\": \"statusMapping\"\n}\n```\n\nThe system automatically applies the lookup during transformation processing.\n\nFor complete details on lookup properties and behavior, see the Lookups schema.\n"},{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the JSON shape the transformTwoDotZero processor\nevaluates `mappings[].extract` JSONPath values against at\nflow runtime. Applies only to Transform 2.0 (v2,\n`rulesTwoDotZero`); v1 transforms (the `rules` array on\n`transform.expression.rules`) and script-mode transforms\nignore this field.\n"}}}}},"script":{"type":"object","description":"Configuration for programmable script-based transformations. This object enables complex, custom\ntransformation logic beyond what expression-based transformations can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to transform data according to\nspecialized business rules or complex algorithms.\n\n**Implementation approach**\n\nScript-based transformation works by:\n1. Executing the specified function from the referenced script\n2. Passing input data to the function\n3. Using the function's return value as the transformed output\n\n**Common use cases**\n\nScript transformation is ideal for:\n- Complex business logic that can't be expressed through mappings\n- Algorithmic transformations requiring computation\n- Dynamic transformations based on external factors\n- Legacy system data format compatibility\n- Multi-stage processing with intermediate steps\n\nOnly use script-based transformation when expression-based transformation is insufficient.\nScript transformation requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to a predefined script resource containing the transformation logic.\n\nThe referenced script should contain the function specified in the\n'function' property. It must be a script in this account: any other id fails the save\nwith 422 (\"Only a script owned by the user or a script shared by the Integration App can\nbe used at transform.script._scriptId.\").\n","format":"objectid"},"function":{"type":"string","description":"Name of the function within the script to execute for transformation. This function\nmust exist in the script referenced by _scriptId.\n"}}}}},"Lookups":{"type":"array","description":"Configuration for value-to-value transformations using lookup tables.\n\n**Purpose**\n\nLookups provide a way to translate values from one system to another. They transform\ninput values into output values using either static mapping tables or\ndynamic lookup caches.\n\n**Lookup mechanisms**\n\nThere are two distinct lookup mechanisms available:\n\n1. **Static Lookups**: Define a simple key-value map object and store it as part of your resource\n   - Best for: Small, fixed sets of values that rarely change\n   - Implementation: Configure the `map` object with input-to-output value mappings\n   - Example: Country codes, status values, simple translations\n\n2. **Dynamic Lookups**: Reference an existing 'Lookup Cache' resource in your Celigo account\n   - Best for: Large datasets, frequently changing values, or complex reference data\n   - Implementation: Configure `_lookupCacheId` to reference cached data maintained independently\n   - Example: Product catalogs, customer databases, pricing information\n\n**Property usage**\n\nThere are two mutually exclusive ways to configure lookups, depending on which mechanism you choose:\n\n1. **For Static Mappings**: Configure the `map` property with a direct key-value object\n   ```json\n   \"map\": {\"US\": \"United States\", \"CA\": \"Canada\"}\n   ```\n\n2. **For Dynamic Lookups**: Configure the following properties:\n   - `_lookupCacheId`: Reference to the lookup cache resource\n   - `extract`: JSON path to extract specific value from the returned lookup object\n\n**When to use**\n\nLookups are ideal for:\n\n1. **Value Translation**: Mapping codes or IDs to human-readable values\n\n2. **Data Enrichment**: Adding related information to records during processing\n\n3. **Normalization**: Ensuring consistent formatting of values across systems\n\n**Implementation details**\n\nLookups can be referenced in:\n\n1. **Field Mappings**: Direct use in field transformation configurations\n\n2. **Handlebars Templates**: Use within templates with the syntax:\n   ```\n   {{lookup 'lookupName' record.fieldName}}\n   ```\n\n**Example usage**\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"countryCodeToName\",\n    \"map\": {\n      \"US\": \"United States\",\n      \"CA\": \"Canada\",\n      \"UK\": \"United Kingdom\"\n    },\n    \"default\": \"Unknown Country\",\n    \"allowFailures\": true\n  },\n  {\n    \"name\": \"productDetails\",\n    \"_lookupCacheId\": \"60a2c4e6f321d800129a1a3c\",\n    \"extract\": \"$.details.price\",\n    \"allowFailures\": false\n  }\n]\n```\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for the lookup table within this configuration.\n\nThis name must be unique within the scope where the lookup is defined and is used to reference\nthe lookup in handlebars templates with the syntax {{lookup 'name' value}}.\n\nChoose descriptive names that indicate the transformation purpose, such as:\n- \"countryCodeToName\" for country code to full name conversion\n- \"statusMapping\" for status code translations\n- \"departmentCodes\" for department code to name mapping\n"},"map":{"type":["object","null"],"description":"The lookup mapping table as key-value pairs. The platform stores `null`\nhere on dynamic lookups, which resolve values at runtime instead of\nfrom a static table.\n\nThis object contains the input values as keys and their corresponding\noutput values. When a input value matches a key in this object,\nit will be replaced with the corresponding value.\n\nThe map should be kept to a reasonable size (typically under 100 entries)\nfor optimal performance. For larger mapping requirements, consider using\ndynamic lookups instead.\n\nMaps can include:\n- Simple code to name conversions: {\"US\": \"United States\"}\n- Status transformations: {\"A\": \"Active\", \"I\": \"Inactive\"}\n- ID to name mappings: {\"100\": \"Marketing\", \"200\": \"Sales\"}\n\nValues can be strings, numbers, or booleans, but all are stored as strings\nin the configuration.\n"},"_lookupCacheId":{"type":"string","description":"Reference to a LookupCache resource that contains the reference data for the lookup.\n\n**Purpose**\n\nThis field connects the lookup to an external data source that has been cached in the system.\nUnlike static lookups that use the `map` property, dynamic lookups can reference large datasets\nor frequently changing information without requiring constant updates to the integration.\n\n**Implementation details**\n\nThe LookupCache resource referenced by this ID contains:\n- The data records to be used as a reference source\n- Configuration for how the data should be indexed and accessed\n- Caching parameters to balance performance with data freshness\n\n**Usage patterns**\n\nCommonly used to reference:\n- Product catalogs or SKU databases\n- Customer or account information\n- Pricing tables or discount rules\n- Complex business logic lookup tables\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n","format":"objectid"},"extract":{"type":"string","description":"JSON path expression that extracts a specific value from the cached lookup object.\n\n**Purpose**\n\nWhen using dynamic lookups with a LookupCache, this JSON path identifies which field to extract\nfrom the cached object after it has been retrieved using the lookup key.\n\n**Implementation details**\n\n- Must use JSON path syntax (similar to mapping extract fields)\n- Operates on the cached object returned by the lookup operation\n- Examples:\n  - \"$.name\" - Extract the name field from the top level\n  - \"$.details.price\" - Extract a nested price field\n  - \"$.attributes[0].value\" - Extract a value from the first element of an array\n\n**Usage scenario**\n\nWhen a lookup cache contains complex objects:\n```json\n// Cache entry for key \"PROD-123\":\n{\n  \"id\": \"PROD-123\",\n  \"name\": \"Premium Widget\",\n  \"details\": {\n    \"price\": 99.99,\n    \"currency\": \"USD\",\n    \"inStock\": true\n  }\n}\n```\n\nSetting extract to \"$.details.price\" would return 99.99 as the lookup result.\n\nIf no extract is provided, the entire cached object is returned as the lookup result.\n"},"default":{"type":["string","null"],"description":"Default value to use when the source value is not found in the lookup map.\nThe platform stores `null` here when no default is configured.\n\nThis value is used as a fallback when:\n1. The source value doesn't match any key in the map\n2. allowFailures is true or omitted\n\nSetting an appropriate default helps prevent flow failures due to unexpected\nvalues and provides predictable behavior for edge cases.\n\nCommon default patterns include:\n- Descriptive unknowns: \"Unknown Country\", \"Unspecified Status\"\n- Original value indicators: \"{Original Value}\", \"No mapping found\"\n- Neutral values: \"Other\", \"N/A\", \"Miscellaneous\"\n\nA save refuses a default together with an explicit `allowFailures: false`\n(422 `Lookups with default set can not have allowFailures explicitly set to\nfalse.`): a lookup either fails a miss (`allowFailures: false`, no default) or\nfalls back to its default.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, missing lookup values will use the default value rather than causing an error.\n\n**Behavior control**\n\nThis field determines how the system handles source values that don't exist in the map:\n\n- true: Use the default value for missing mappings and continue processing\n- false: Treat missing mappings as errors, failing the record (`lookup_error` for a\n  static map, `lookupcache_evaluation_failed` for a lookup cache)\n- omitted or null: the record doesn't fail either. On an import a static lookup writes\n  its default if it has one, and a static or cache lookup without one leaves the\n  mapped field out of the output, with no error\n\n**Recommendation**\n\nSet this to true when:\n- New source values might appear over time\n- Data quality issues could introduce unexpected values\n- Processing should continue even with imperfect mapping\n\nSet this to false when:\n- Complete data accuracy is critical\n- All possible source values are known and controlled\n- Missing mappings indicate serious data problems that should be addressed\n\nA default raises no error: records that miss go out with the default value and\nnothing reports them. Use true only when that fallback is acceptable at the\ndestination, and set false explicitly when a missing value must stop the record.\n"}}}},"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: one in the script named by `_scriptId`, or one the code deployed on\nthe stack named by `_stackId` exposes."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`. Send this or `_stackId`, not both.\nMust be a script in this account: any other id fails the save with 422, naming the hook,\nsuch as \"Only a script owned by the user or a script shared by the Integration App can\nbe used at hooks.preMap._scriptId.\" on an import's `preMap`."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments.\nThe API doesn't enforce the either/or: a save carrying both `_scriptId` and `_stackId`\nanswers 200 and stores both, and the script and the stack then each list the step as a\ndependent."},"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."}}},"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"},"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"]},"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"}}},"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/imports":{"get":{"summary":"List AI agents","description":"Returns all imports in the account. The response includes every import type,\nnot just AI agents — filter client-side by `adaptorType: \"AiAgentImport\"`.","operationId":"listAiAgents","tags":["AI Agents"],"parameters":[{"$ref":"#/components/parameters/Include"},{"$ref":"#/components/parameters/Exclude"},{"$ref":"#/components/parameters/After"},{"name":"limit","in":"query","required":false,"description":"Maximum number of imports to return per page.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":1000}}],"responses":{"200":{"description":"Array of import objects.","headers":{"Link":{"description":"RFC 5988 pagination links. Includes `<...>; rel=\"next\"` when more\npages remain; absent on the final page.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AiAgent"}}}}},"204":{"description":"No imports exist in the account."},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"}}}}}}
````

## Create an AI agent

> Creates a new AI agent import. The provider decides the configuration\
> format: \`aiAgent.openai\` for OpenAI, \`aiAgent.litellm\` for Gemini and\
> Anthropic Claude, or the generic flat format (\`aiAgent.model\`,\
> \`aiAgent.modelOptions\`, …) for the catalog providers Mistral, xAI,\
> Hugging Face, DeepSeek, Cohere, and Groq — a block that does not match the\
> provider is rejected. For a built-in provider, omit \`\_connectionId\` to\
> use platform-managed credentials or include it for BYOK; a catalog\
> provider needs a BYOK connection to save — without one the request\
> fails with 422 \`connection\_required\` on \`\_connectionId\`.

````json
{"openapi":"3.2.0","info":{"title":"AI Agents","version":"1.0.0"},"tags":[{"name":"AI Agents","description":"AI agents are LLM-powered import steps that classify, extract, validate, or\ngenerate data within integration flows. They are stored as import resources\nwith `adaptorType: AiAgentImport` and managed via the `/v1/imports` endpoints.\n\nThree built-in providers — OpenAI (via the Responses API), Google Gemini (via\nLiteLLM), and Anthropic Claude (via LiteLLM) — run on platform-managed\ncredentials or, with a `_connectionId`, on your own key (BYOK). Catalog\nproviders — Mistral, xAI, Hugging Face, DeepSeek, Cohere, and Groq — use one generic\nflat configuration format and always run on a BYOK connection.\n\n## AI agent schema\n\n{% openapi-schemas spec=\"ai-agent\" schemas=\"AiAgent\" 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":"Configuration for an AI agent import. Set `aiAgent.provider` to choose\nthe LLM backend, then configure the matching format: `aiAgent.openai` for\nOpenAI, `aiAgent.litellm` for Gemini and Anthropic Claude, or the generic\nflat format (`aiAgent.model`, `aiAgent.modelOptions`, …) for the catalog\nproviders. Omit `_connectionId` to use platform-managed credentials on a\nbuilt-in provider; catalog providers need a BYOK connection to run.","required":["name","adaptorType","aiAgent"],"allOf":[{"$ref":"#/components/schemas/AiAgentBase"}]},"AiAgentBase":{"type":"object","description":"Writable AI agent import fields shared by the request and response schemas. Set\n`aiAgent.provider` to choose the LLM backend, then configure the matching format:\n`aiAgent.openai` for OpenAI, `aiAgent.litellm` for Gemini and Anthropic Claude\n(with provider-specific settings under `aiAgent.litellm._overrides`), or the\ngeneric flat format (`aiAgent.model`, `aiAgent.modelOptions`, …) for the catalog\nproviders Mistral, xAI, Hugging Face, DeepSeek, Cohere, and Groq.","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Display name for the AI agent."},"description":{"type":"string","description":"Optional description of the AI agent's purpose."},"_connectionId":{"type":"string","format":"objectId","description":"Connection for BYOK (bring your own key) — an HTTP connection to the vendor's API.\nOptional for the built-in providers (`openai`, `gemini`, `anthropic`), which run on\nplatform-managed credentials when it is omitted. Catalog providers (`mistral`, `xai`,\n`huggingface`, `deepseek`, `cohere`, `groq`) have no platform-managed credentials: the save\nrequires it — without one the agent fails to save with 422 `connection_required` on\n`_connectionId`."},"_integrationId":{"type":"string","format":"objectId","description":"Integration this AI agent belongs to. Omitted for a standalone agent."},"adaptorType":{"type":"string","enum":["AiAgentImport"],"description":"Must be `AiAgentImport`."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this AI agent was created from, set when it is part of an installed integration app."},"aiAgent":{"$ref":"#/components/schemas/AiAgentConfig"},"mappings":{"allOf":[{"$ref":"#/components/schemas/Mappings"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Selects the data context that `mappings[].extract` evaluates against. With no\nmappings the agent receives the entire record as JSON (or only the record's own\ntop-level `text`, `record` or `files` fields, when it has any) and `inputContext`\nis moot. `record`\n(the default) extracts against the flat record; `envelope` extracts against the\nruntime envelope, which exposes the record alongside job, settings, and other\nrun-time metadata (`$.record.field`, `$.settings.field`, etc.). Same contract as\nthe import resource's `inputContext`."},"filter":{"description":"Filter applied to incoming records before they are sent to the LLM. Records that match\ncontinue through the agent; records that don't skip it, counted as ignored with no model\ncall, and continue to the next step.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"responseTransform":{"description":"Transformation that reshapes the LLM's response after records are processed, before the\nresponse is handled by response mappings and downstream steps.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the agent's import lifecycle.\nAI agents do not aggregate records, so the `postAggregate` hook that other import\ntypes expose is intentionally omitted here.","properties":{"preMap":{"description":"Hook that runs on each page of records before the agent's mappings are applied.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postMap":{"description":"Hook that runs after mappings are applied, before records are sent to the LLM.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postSubmit":{"description":"Hook that runs after the LLM responds, with both the submitted records and the responses.","allOf":[{"$ref":"#/components/schemas/Hook"}]}}},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"distributed":{"type":"boolean","description":"Always false for AI agent imports — the agent executes on Celigo's servers, not inside a target application."},"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 are silently skipped. Rarely meaningful for AI agents; accepted for import-surface parity."},"ignoreMissing":{"type":"boolean","description":"When true, records that do not already exist in the destination are silently skipped. Rarely meaningful for AI agents; accepted for import-surface parity."},"mockResponse":{"type":"array","description":"Predefined response records used in place of calling the LLM when testing the agent,\nso flows can run without spending tokens. Records must be in integrator.io canonical\nformat — 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 for structured-output agents."},"_text":{"type":"string","description":"Mock response body for text-output agents."},"_headers":{"type":"object","description":"Mock response headers."},"blobKey":{"type":"string","description":"Mock output of an agent with `blob` output (image generation:\n`openai.output.format.type` or `litellm.responseFormat.type` set to `blob`),\nwhich returns the stored image's key here. Unrelated to the import-level `blob`\nflag, which agents refuse."}}}}}},"AiAgentConfig":{"type":"object","description":"AI agent configuration for an AiAgentImport. An `ai_agent` guardrail is configured by\nthe guardrail spec's `GuardrailAiAgentConfig` instead, which has no `tools` arrays.\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 the\nsave requires one — without it the agent fails to save with 422\n`connection_required` on `_connectionId`.\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","max"],"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":"Caps the length of the model's response, in tokens. An agent created without it is\nsaved with 5000; raise it for long outputs such as large extractions."},"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":"Which editor the UI opens `jsonSchema` in; the schema sent to the provider\nis the same either way, and the field is only meaningful when `type` is\n\"json_schema\". Unset opens the visual editor, and an edit there rebuilds\n`jsonSchema` from the field list, keeping only `type`, `properties`,\n`required`, `items`, `description`, `enum`, `pattern`, length and numeric\nbounds, `multipleOf`, `minItems`/`maxItems` and a `\"null\"` type — other\nkeywords are dropped without a prompt, and `integer` becomes `number`."},"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`."}}},"schema":{"type":"object","readOnly":true,"additionalProperties":true,"description":"Server-written copy of `jsonSchema`, returned whenever `jsonSchema` is set — on\nAI agent imports and on guardrail agents alike. Send `jsonSchema`, not this field."}},"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","code_execution","tool"],"description":"Type of tool. Every type except `code_execution` is configured through the\nsibling object of the same name; OpenAI spells this one `code_execution`, while\nthe Gemini and Anthropic overrides spell theirs `codeExecution` — the other spelling\nfails with 422 `enum`."},"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"]}}}},"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":"Which editor the UI opens `jsonSchema` in; the schema sent to the provider is\nthe same either way, and the field is only meaningful when `type` is\n\"json_schema\". Unset opens the visual editor, and an edit there rebuilds\n`jsonSchema` from the field list, keeping only `type`, `properties`, `required`,\n`items`, `description`, `enum`, `pattern`, length and numeric bounds,\n`multipleOf`, `minItems`/`maxItems` and a `\"null\"` type — other keywords are\ndropped without a prompt, and `integer` becomes `number`."},"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","codeExecution","mcp","tool"],"description":"Type of Gemini tool. `codeExecution` is the Gemini spelling — `code_execution`\nfails with 422 `enum`."},"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"]}}}},"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","codeExecution"],"description":"Type of Claude tool. `codeExecution` is the Anthropic spelling — `code_execution`\nfails with 422 `enum`."},"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). Each\nentry is either a plain tool name or an object carrying display\nmetadata — same contract as the OpenAI and Gemini `allowedTools`.","items":{"oneOf":[{"title":"name","type":"string","description":"Tool name as exposed by the MCP server."},{"title":"tool","type":"object","required":["name"],"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."}}}]}}}},"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."}}}}}}}}},"Mappings":{"type":"array","description":"Array of field mapping configurations for transforming data from one format into another.\n\n**Guidance**\n\nThis schema is designed around RECURSION as its core architectural principle. Understanding this recursive\nnature is essential for building effective mappings:\n\n1. The schema is self-referential by design - a mapping can contain nested mappings of the same structure\n2. Complex data structures (nested objects and arrays of objects, to any depth) are ALL\n   handled through this recursive pattern; an array of arrays is not (build one in a script hook)\n3. Each mapping handles one level of the data structure; deeper levels are handled by nested mappings\n\nWhen generating mappings programmatically:\n- For simple fields (string, number, boolean): Create single mapping objects\n- For objects: Create a parent mapping with nested 'mappings' array containing child field mappings\n- For arrays: Use 'buildArrayHelper' with extract paths defining array inputs and\n  recursive 'mappings' to define object structures\n\nThe system will process these nested structures recursively during runtime, ensuring proper construction\nof complex hierarchical data while maintaining excellent performance.\n\nOn save, a leaf row with neither a source (`extract`, `hardCodedValue`, `lookupName`) nor a\ndestination (`generate`) is dropped silently, at every nesting level, and so is an object or\nobject-array row left with no source, no destination and no remaining child `mappings` or\n`buildArrayHelper` entries; a row with only one of the two, and a source-less, destination-less\nwrapper that still has children, are kept.\n\nThe shape of the array is the mapper's **JSON structure**. Named top-level items produce an object\nrecord (`record { }`). Exactly one top-level item with `dataType: objectarray` and no `generate` is\nthe `rows [ ]` structure: the mapped record is an array — usually one element per element of the\nsource array that root's `buildArrayHelper` iterates — Handlebars templates such as `http.body`\nbind it as `rows` instead of `record`, and the mapping editor shows that row's destination disabled.\n","items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}}},"items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}},"Filter":{"type":"object","description":"Configuration for selectively processing records based on specified criteria. This object enables\nprecise control over which items are included or excluded from processing operations.\n\n**Filter behavior**\n\nWhen configured, the filter is applied before processing begins:\n- Items that match the filter criteria are processed\n- Items that don't match are completely skipped\n- No partial processing is performed\n\nSend the whole object. A rules array sent alone as the `filter` value is not\nstored, and a full update of an export that carries one removes the filter the\nexport had, with no error.\n\n**Implementation approaches**\n\nThere are two distinct filtering mechanisms available:\n\n**Rule-Based Filtering (`type: \"expression\"`)**\n- **Best For**: Common filtering patterns based on standard attributes\n- **Capabilities**: Filter by names, values, dates, numerical ranges, text patterns\n- **Advantages**: Declarative, no coding required, consistent performance\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear, static criteria for selection\n\n**Script-Based Filtering (`type: \"script\"`)**\n- **Best For**: Complex logic, dynamic criteria, or business rules\n- **Capabilities**: Full programmatic control, access to complete metadata\n- **Advantages**: Maximum flexibility, can implement any filtering logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Simple rules aren't sufficient or logic needs to be dynamic\n","properties":{"type":{"type":"string","description":"Determines which filtering mechanism to use. This choice affects which properties\nmust be configured and how filtering logic is implemented.\n\n**Available types**\n\n**Rule-Based Filtering (`\"expression\"`)**\n- **Required Config**: The `expression` object with rule definitions\n- **Behavior**: Evaluates declarative rules against item attributes\n- **Best For**: Common patterns like name matching, date ranges, value limits\n- **Advantages**: Simpler to configure, no custom code required\n\n**Script-Based Filtering (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to determine which items to process\n- **Best For**: Complex conditions, business logic, dynamic criteria\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard filtering needs (name, size, date), use `\"expression\"`\n2. For complex logic or conditions not covered by expressions, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based filtering. This object enables filtering\nitems based on common attributes without requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define filtering rules that\ncan match against item attributes like name, type, value, date, and other properties.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Rules can be combined with AND/OR logic\n- Each rule can check a specific attribute\n- Multiple conditions can be applied (ranges, pattern matching, exact matches)\n\n**Common filter patterns**\n\n1. **Pattern matching**: Regular expressions with `matches`\n2. **Value range filtering**: Numbers between min and max values\n3. **Date range filtering**: Items created/modified within specific time ranges\n4. **Status checking**: Items with specific status values or properties\n\nFor AI agents: Rule-based filtering should be your first choice when the filtering criteria\ncan be expressed in terms of standard attributes. Only use script-based filtering when\nmore complex logic is required.\n","properties":{"version":{"type":"string","description":"Version identifier for the expression format. Currently only version \"1\" is supported.\n\nThis field ensures future compatibility if the expression format evolves. Always set to \"1\"\nfor current implementations.\n","enum":["1"]},"rules":{"type":"array","description":"Expression array defining filter conditions using prefix notation. The first element is the operator,\nfollowed by its operands which may themselves be nested expression arrays.\n\nThe rule expression follows this pattern:\n- First element: Operator name (string)\n- Remaining elements: Operands for that operator (values or nested expressions)\n\n**Expression structure**\n\nFilter expressions use a prefix notation where operators appear before their operands:\n```\n[operator, operand1, operand2, ...]\n```\n\n**Comparison Operators**\n- `\"equals\"`: Exact match (equals)\n- `\"notequals\"`: Not equal to value (not equals)\n- `\"greaterthan\"`: Value is greater than specified value (is greater than)\n- `\"greaterthanequals\"`: Value is greater than or equal to specified value (is greater than or equals)\n- `\"lessthan\"`: Value is less than specified value (is less than)\n- `\"lessthanequals\"`: Value is less than or equal to specified value (is less than or equals)\n- `\"startswith\"`: String starts with specified prefix (starts with)\n- `\"notstartswith\"`: String does not start with specified prefix (does not start with)\n- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"notendswith\"`: String does not end with specified suffix (does not end with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"empty\"`: Field is missing or empty (is empty)\n- `\"notempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches a regular expression, ignoring case (matches)\n\nOperator names are case-insensitive: `NOTEMPTY` and `endsWith` work and are stored\nas sent. `isempty`, `isnotempty`, `anyof`, `notcontains`, `doesnotstartwith`,\n`doesnotendwith`, `notmatches`, `doesnotmatch` and `notequal` are not operators:\nthey are refused with 422 \"Invalid operation: <name>\", with the name in lowercase,\nboth when the filter is saved and by `POST /v1/processors/filter`. To negate a\ncondition, wrap it in `not`.\n\nThe string comparisons (`equals`, `contains`, `startswith`, `endswith` and their\nnegations) are case-sensitive; wrap the field in `lowercase` to compare without case.\n`matches` is not: every pattern is compiled with the flags `gi`, so `^paid$` also\nmatches `PAID` and `[a-z]` matches capital letters. An inline flag such as `(?i)`\nmakes the pattern invalid. The filter still saves, but in a run every record fails\nwith `FILTER_EVALUATION_FAILED` (\"Invalid regular expression: /(?i)…/gi: Invalid\ngroup\"), and `POST /v1/processors/filter` refuses the call with 422 (live-verified\n2026-10-22).\n\n**Logical Operators**\n- `\"and\"`: All conditions must be true\n- `\"or\"`: At least one condition must be true\n- `\"not\"`: Negates the condition\n\n**Field Access and Type Conversion**\n- `\"extract\"`: Access a field from the item by name\n- `\"settings\"`: Access a custom setting from the flow, flow step, or integration configuration\n- `\"boolean\"`: Convert value to Boolean type\n- `\"epochtime\"`: Convert value to Epoch Time (Unix timestamp)\n- `\"number\"`: Convert value to Number type\n- `\"string\"`: Convert value to String type\n\n**Field Access Details**\n\n**Using `extract` to access record fields:**\n- Retrieves values from the current record being processed\n- Can access nested properties using dot notation (e.g., `\"customer.email\"`)\n- Paths are relative to the record itself: `[\"extract\", \"record.status\"]`\n  matches nothing\n- In a record filter, must be wrapped in a type conversion (`string`, `number`,\n  `boolean` or `epochtime`): a bare `[\"extract\", ...]` operand is refused with 422\n  \"Field operation extract must be called by a Type Conversion Operation but was called\n  by <operator>\", both when an import's filter is saved and by\n  `POST /v1/processors/filter`\n\n**Using `settings` to access configuration values:**\n- Retrieves values from the integration's configuration settings\n- Supports different scopes with prefix notation:\n  - `flow.settingName`: Access flow-level settings\n  - `flowGrouping.settingName`: Access the flow group's settings\n  - `export.settingName`: Access the export's (or lookup's) own settings\n  - `import.settingName`: Access the import's own settings\n  - `integration.settingName`: Access integration-level settings\n  - `connection.settingName`, `iClient.settingName`: Access the step's\n    connection and iClient settings\n- A scope whose resource has no settings can be absent; a rule that reads\n  it matches nothing\n- Must be wrapped in a type conversion like `extract`: a bare\n  `[\"settings\", ...]` operand is refused with 422 \"Field operation settings\n  must be called by a Type Conversion Operation\"\n- Useful for dynamic filtering based on configuration\n\n**Field Transformations**\n- `\"lowercase\"`: Convert string to lowercase\n- `\"uppercase\"`: Convert string to uppercase\n- `\"ceiling\"`: Round number up to the nearest integer\n- `\"floor\"`: Round number down to the nearest integer\n- `\"abs\"`: Get absolute value of a number\n\n**Arithmetic** (number operands)\n- `\"add\"`, `\"subtract\"`, `\"multiply\"`, `\"divide\"`, `\"modulo\"`: for example\n  `[\"subtract\", [\"number\", [\"extract\", \"total\"]], [\"number\", [\"extract\", \"discount\"]]]`.\n  `mod` is not an operator (422 \"Invalid operation: mod.\").\n\n**Dates**\n- `epochtime` reads ISO 8601 strings and epoch-millisecond numbers. An empty\n  string, a string that isn't a date, or epoch milliseconds sent as a string\n  fail it with \"Invalid date. Cannot convert given string or number to date\n  time.\", followed by `; record=` and the record as JSON: in a run that record fails (`FILTER_EVALUATION_FAILED`, source\n  `output_filter` on an export), and `POST /v1/processors/filter` and an\n  export preview refuse the whole call with 422. `and` and `or` evaluate\n  every operand, so a `notempty` test beside it doesn't prevent the failure.\n  A missing or null field doesn't match.\n- `datetime`, `date` and `timestamp` are not type conversions: 422\n  \"Invalid operation: <name>.\"\n- `number` reads a missing or empty field as 0.\n\n**Handlebars operands**\n- An operand can also be a string holding exactly one Handlebars expression, such as\n  `\"{{record.status}}\"` or `\"{{lowercase record.code}}\"`, on either side of the\n  operator. It renders against the record (`record.<field>`; an unprefixed\n  `{{status}}` renders empty) before the comparison.\n- The rendered value is a string: wrap it in `[\"number\", ...]` to compare it with a\n  number, or the rule is refused with 422.\n- `epochtime` does not render Handlebars: `[\"epochtime\", \"{{timestamp}}\"]` is refused\n  with 422 \"invalid datetime format\".\n- A string with text around the expression, or with more than one expression, is\n  compared as a literal.\n- `[\"number\", \"{{timestamp \\\"x\\\"}}\"]` is the current time in epoch milliseconds,\n  so `[\"greaterthanequals\", [\"epochtime\", [\"extract\", \"created\"]], [\"subtract\",\n  [\"number\", \"{{timestamp \\\"x\\\"}}\"], 604800000]]` keeps records created in the\n  last 7 days.\n- An unknown helper fails the rule with 422 \"Failed to evaluate handlebars\n  expression\".\n\nChoose the type conversion that matches the comparison — `number` for numeric\ncomparisons, `epochtime` for dates, `string` for text. For example:\n\n```json\n[\n  \"equals\",\n  [\n    \"number\",  // Convert to number before comparison\n    [\n      \"extract\",\n      \"quantity\"\n    ]\n  ],\n  100\n]\n```\n\nExample with datetime conversion:\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",  // Convert to Unix timestamp before comparison\n    [\n      \"extract\",\n      \"createdDate\"\n    ]\n  ],\n  1609459200000  // January 1, 2021 as Unix timestamp in milliseconds\n]\n```\n\nExample with transformations:\n```json\n[\n  \"and\",\n  [\n    \"matches\",\n    [\n      \"lowercase\",  // Convert to lowercase before matching\n      [\n        \"string\",\n        [\n          \"extract\",\n          \"categories\"\n        ]\n      ]\n    ],\n    \"netsuite\"\n  ],\n  [\n    \"notequals\",\n    [\n      \"string\",\n      [\n        \"extract\",\n        \"recurrence.pattern.type\"\n      ]\n    ],\n    \"\"\n  ]\n]\n```\n\nExample comparing a record field with a flow setting:\n```json\n[\n  \"equals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"trantype\"\n    ]\n  ],\n  [\n    \"string\",\n    [\n      \"settings\",\n      \"flow.trantype\"\n    ]\n  ]\n]\n```\n\n**Examples**\n\nExample 1: Status field is not equal to \"cancelled\"\n```json\n[\n  \"notequals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"status\"\n    ]\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"filename\"\n    ]\n  ],\n  \"HC\"\n]\n```\n\nExample 3: Amount is greater than 100\n```json\n[\n  \"greaterthan\",\n  [\n    \"number\",\n    [\n      \"extract\",\n      \"amount\"\n    ]\n  ],\n  100\n]\n```\n\nExample 4: Order date is after January 1, 2023 (UTC). Compare dates as `epochtime`\nagainst epoch milliseconds: a `string` comparison of ISO timestamps orders values with\ndifferent UTC offsets wrongly.\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",\n    [\n      \"extract\",\n      \"orderDate\"\n    ]\n  ],\n  1672531200000\n]\n```\n","items":{"oneOf":[{"title":"String","type":"string"},{"title":"Number","type":"number"},{"title":"Boolean","type":"boolean"},{"title":"Object","type":"object"},{"title":"Array","type":"array"},{"title":"Null","type":"null"}]}}}},"script":{"type":"object","description":"Configuration for programmable script-based filtering. This object enables complex, custom\nfiltering logic beyond what expression-based filtering can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to determine which items\nshould be processed.\n\n**Implementation approach**\n\nScript-based filtering works by:\n1. Executing the specified function from the referenced script\n2. Passing item data to the function\n3. Using the function's return value (true/false) to determine inclusion\n\n**Common use cases**\n\nScript filtering is ideal for:\n- Complex business logic that can't be expressed as simple rules\n- Dynamic filtering criteria that change based on external factors\n- Content-based filtering that requires deep inspection\n- Advanced pattern matching beyond simple wildcards\n- Multi-stage filtering with intermediate logic\n\nFor AI agents: Only use script-based filtering when expression-based filtering is insufficient.\nScript filtering requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to the Script resource that contains the filtering logic. This must be a valid\nObjectId of a Script resource in this account: any other id fails the save with 422\n(\"Only a script owned by the user or a script shared by the Integration App can be used\nat filter.script._scriptId.\").\n\nThe referenced script must contain the function specified in the `function` field\nand must be written to handle filtering specifically. The script receives\nitem data as its input and must return a boolean value indicating whether\nto process the item (true) or skip it (false).\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n"},"function":{"type":"string","description":"Name of the function within the script to execute for filtering decisions. This function\nmust exist in the script referenced by _scriptId.\n\n**Function requirements**\n\nThe specified function must:\n- Accept item data as its first parameter\n- Return a boolean value (true to process the item, false to skip it)\n- Handle errors gracefully\n- Execute efficiently (as it may run for many items)\n\n**Function signature**\n\n```javascript\nfunction filterItems(itemData) {\n  // itemData contains properties of the item being evaluated\n  // Custom logic here\n  return true; // or false to skip the item\n}\n```\n\nFor AI agents: Ensure the function name exactly matches a function defined in the\nreferenced script, as mismatches will cause the filter to fail.\n"}}}}},"Transform":{"type":"object","description":"Configuration for transforming data during processing operations. This object enables\nreshaping of records.\n\n**Transformation capabilities**\n\nCeligo's transformation engine offers powerful features for data manipulation:\n- Precise field mapping with JSONPath expressions\n- Support for any level of nested arrays\n- Formula-based field value generation\n- Dynamic references to flow and integration settings\n\n**Implementation approaches**\n\nThere are two distinct transformation mechanisms available:\n\n**Rule-Based Transformation (`type: \"expression\"`)**\n- **Best For**: Most transformation scenarios from simple to complex\n- **Capabilities**: Field mapping, formula calculations, lookups, nested data handling\n- **Advantages**: Visual configuration, no coding required, intuitive interface\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear mapping requirements or need to reshape data structure\n\n**Script-Based Transformation (`type: \"script\"`)**\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Capabilities**: Full programmatic control, custom processing, complex business rules\n- **Advantages**: Maximum flexibility, can implement any transformation logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Visual transformation tools aren't sufficient for your use case\n","properties":{"type":{"type":"string","description":"Determines which transformation mechanism to use. This choice affects which properties\nmust be configured and how transformation logic is implemented.\n\n**Available types**\n\n**Rule-Based Transformation (`\"expression\"`)**\n- **Required Config**: The `expression` object with mapping definitions\n- **Behavior**: Applies declarative rules to reshape data\n- **Best For**: Most transformation scenarios from simple to complex\n- **Advantages**: Visual configuration, no coding required\n\n**Script-Based Transformation (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to transform data\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard data transformations, use `\"expression\"`\n2. For complex logic or specialized processing, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based transformations. This object enables reshaping data\nwithout requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define transformation rules that\ncan map, modify, and generate data elements.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Field mappings define how input data is transformed to target fields\n- Formulas can be used to calculate or generate new values\n- Lookups can enrich data by fetching related information\n- Mode determines how records are processed (create new or modify existing)\n","properties":{"version":{"type":"string","description":"Version of the expression format. Determines which rules\nproperty contains the transformation logic.\n","enum":["1","2"]},"rules":{"type":"array","description":"Transformation rules for version 1 expressions. An array of\nrule groups; each group is an array of field-mapping objects.\nMost transforms have a single group. Present when `version`\nis `\"1\"`. The output record contains ONLY the generated\nfields — every unmapped field is dropped (v1 has no\nequivalent of Transform 2.0's `modify` mode), and the\nrecord's trace key does not survive the rebuild.\n","items":{"type":"array","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path to read from. Supports multiple\nsyntaxes: bare field names (`id`), dot notation\n(`fulfillment.shipment_id`), slash-prefixed paths\nfor XML (`/FeedProcessingStatus`), wildcards (`*.id`,\n`*.[Internal ID]`), and array indexing (`SDF[0]`).\n"},"generate":{"type":"string","description":"Target field name to write to. Typically a bare name\n(`id`) or dot path (`SDF.Filter.ID`).\n"},"key":{"type":"string","description":"Auto-generated identifier for this rule, used by the\nUI to track individual rules for editing and reordering.\n"}},"required":["extract","generate"]}}},"rulesTwoDotZero":{"type":"object","description":"Configuration for version 2 transformation rules. This object contains the core logic\nfor how data is mapped, enriched, and transformed.\n\n**Capabilities**\n\nTransformation 2.0 provides:\n- Precise field mapping with JSONPath expressions\n- Support for deeply nested data structures\n- Formula-based field generation\n- Dynamic lookups for data enrichment\n- Multiple operating modes to fit different scenarios\n","properties":{"mode":{"type":"string","description":"Transformation mode that determines how records are handled during processing.\n\n**Available modes**\n\n**Create Mode (`\"create\"`)**\n- **Behavior**: Builds entirely new output records from inputs\n- **Use When**: Output structure differs significantly from input\n- **Advantage**: Clean slate approach, no field inheritance\n\n**Modify Mode (`\"modify\"`)**\n- **Behavior**: Makes targeted edits to existing records\n- **Use When**: Output structure should remain similar to input\n- **Advantage**: Preserves unmapped fields from the original record\n","enum":["create","modify"]},"mappings":{"$ref":"#/components/schemas/Mappings"},"lookups":{"allOf":[{"description":"Shared lookup tables used across all mappings defined in the transformation rules.\n\n**Purpose**\n\nLookups provide centralized value translation that can be referenced from any mapping\nin your transformation configuration. They enable consistent translation of codes, IDs,\nand values between systems without duplicating translation logic.\n\n**Usage in transformations**\n\nLookups are particularly valuable in transformations for:\n\n- **Data Normalization**: Standardizing values from diverse source systems\n- **Code Translation**: Converting between different coding systems (e.g., status codes)\n- **Field Enrichment**: Adding descriptive values based on ID or code lookups\n- **Cross-Reference Resolution**: Mapping identifiers between integrated systems\n\n**Implementation**\n\nLookups are defined once in this array and referenced by name in mappings:\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"statusMapping\",\n    \"map\": {\n      \"A\": \"Active\",\n      \"I\": \"Inactive\",\n      \"P\": \"Pending\"\n    },\n    \"default\": \"Unknown Status\"\n  }\n]\n```\n\nThen referenced in mappings using the lookupName property:\n\n```json\n{\n  \"generate\": \"status\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.statusCode\",\n  \"lookupName\": \"statusMapping\"\n}\n```\n\nThe system automatically applies the lookup during transformation processing.\n\nFor complete details on lookup properties and behavior, see the Lookups schema.\n"},{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the JSON shape the transformTwoDotZero processor\nevaluates `mappings[].extract` JSONPath values against at\nflow runtime. Applies only to Transform 2.0 (v2,\n`rulesTwoDotZero`); v1 transforms (the `rules` array on\n`transform.expression.rules`) and script-mode transforms\nignore this field.\n"}}}}},"script":{"type":"object","description":"Configuration for programmable script-based transformations. This object enables complex, custom\ntransformation logic beyond what expression-based transformations can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to transform data according to\nspecialized business rules or complex algorithms.\n\n**Implementation approach**\n\nScript-based transformation works by:\n1. Executing the specified function from the referenced script\n2. Passing input data to the function\n3. Using the function's return value as the transformed output\n\n**Common use cases**\n\nScript transformation is ideal for:\n- Complex business logic that can't be expressed through mappings\n- Algorithmic transformations requiring computation\n- Dynamic transformations based on external factors\n- Legacy system data format compatibility\n- Multi-stage processing with intermediate steps\n\nOnly use script-based transformation when expression-based transformation is insufficient.\nScript transformation requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to a predefined script resource containing the transformation logic.\n\nThe referenced script should contain the function specified in the\n'function' property. It must be a script in this account: any other id fails the save\nwith 422 (\"Only a script owned by the user or a script shared by the Integration App can\nbe used at transform.script._scriptId.\").\n","format":"objectid"},"function":{"type":"string","description":"Name of the function within the script to execute for transformation. This function\nmust exist in the script referenced by _scriptId.\n"}}}}},"Lookups":{"type":"array","description":"Configuration for value-to-value transformations using lookup tables.\n\n**Purpose**\n\nLookups provide a way to translate values from one system to another. They transform\ninput values into output values using either static mapping tables or\ndynamic lookup caches.\n\n**Lookup mechanisms**\n\nThere are two distinct lookup mechanisms available:\n\n1. **Static Lookups**: Define a simple key-value map object and store it as part of your resource\n   - Best for: Small, fixed sets of values that rarely change\n   - Implementation: Configure the `map` object with input-to-output value mappings\n   - Example: Country codes, status values, simple translations\n\n2. **Dynamic Lookups**: Reference an existing 'Lookup Cache' resource in your Celigo account\n   - Best for: Large datasets, frequently changing values, or complex reference data\n   - Implementation: Configure `_lookupCacheId` to reference cached data maintained independently\n   - Example: Product catalogs, customer databases, pricing information\n\n**Property usage**\n\nThere are two mutually exclusive ways to configure lookups, depending on which mechanism you choose:\n\n1. **For Static Mappings**: Configure the `map` property with a direct key-value object\n   ```json\n   \"map\": {\"US\": \"United States\", \"CA\": \"Canada\"}\n   ```\n\n2. **For Dynamic Lookups**: Configure the following properties:\n   - `_lookupCacheId`: Reference to the lookup cache resource\n   - `extract`: JSON path to extract specific value from the returned lookup object\n\n**When to use**\n\nLookups are ideal for:\n\n1. **Value Translation**: Mapping codes or IDs to human-readable values\n\n2. **Data Enrichment**: Adding related information to records during processing\n\n3. **Normalization**: Ensuring consistent formatting of values across systems\n\n**Implementation details**\n\nLookups can be referenced in:\n\n1. **Field Mappings**: Direct use in field transformation configurations\n\n2. **Handlebars Templates**: Use within templates with the syntax:\n   ```\n   {{lookup 'lookupName' record.fieldName}}\n   ```\n\n**Example usage**\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"countryCodeToName\",\n    \"map\": {\n      \"US\": \"United States\",\n      \"CA\": \"Canada\",\n      \"UK\": \"United Kingdom\"\n    },\n    \"default\": \"Unknown Country\",\n    \"allowFailures\": true\n  },\n  {\n    \"name\": \"productDetails\",\n    \"_lookupCacheId\": \"60a2c4e6f321d800129a1a3c\",\n    \"extract\": \"$.details.price\",\n    \"allowFailures\": false\n  }\n]\n```\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for the lookup table within this configuration.\n\nThis name must be unique within the scope where the lookup is defined and is used to reference\nthe lookup in handlebars templates with the syntax {{lookup 'name' value}}.\n\nChoose descriptive names that indicate the transformation purpose, such as:\n- \"countryCodeToName\" for country code to full name conversion\n- \"statusMapping\" for status code translations\n- \"departmentCodes\" for department code to name mapping\n"},"map":{"type":["object","null"],"description":"The lookup mapping table as key-value pairs. The platform stores `null`\nhere on dynamic lookups, which resolve values at runtime instead of\nfrom a static table.\n\nThis object contains the input values as keys and their corresponding\noutput values. When a input value matches a key in this object,\nit will be replaced with the corresponding value.\n\nThe map should be kept to a reasonable size (typically under 100 entries)\nfor optimal performance. For larger mapping requirements, consider using\ndynamic lookups instead.\n\nMaps can include:\n- Simple code to name conversions: {\"US\": \"United States\"}\n- Status transformations: {\"A\": \"Active\", \"I\": \"Inactive\"}\n- ID to name mappings: {\"100\": \"Marketing\", \"200\": \"Sales\"}\n\nValues can be strings, numbers, or booleans, but all are stored as strings\nin the configuration.\n"},"_lookupCacheId":{"type":"string","description":"Reference to a LookupCache resource that contains the reference data for the lookup.\n\n**Purpose**\n\nThis field connects the lookup to an external data source that has been cached in the system.\nUnlike static lookups that use the `map` property, dynamic lookups can reference large datasets\nor frequently changing information without requiring constant updates to the integration.\n\n**Implementation details**\n\nThe LookupCache resource referenced by this ID contains:\n- The data records to be used as a reference source\n- Configuration for how the data should be indexed and accessed\n- Caching parameters to balance performance with data freshness\n\n**Usage patterns**\n\nCommonly used to reference:\n- Product catalogs or SKU databases\n- Customer or account information\n- Pricing tables or discount rules\n- Complex business logic lookup tables\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n","format":"objectid"},"extract":{"type":"string","description":"JSON path expression that extracts a specific value from the cached lookup object.\n\n**Purpose**\n\nWhen using dynamic lookups with a LookupCache, this JSON path identifies which field to extract\nfrom the cached object after it has been retrieved using the lookup key.\n\n**Implementation details**\n\n- Must use JSON path syntax (similar to mapping extract fields)\n- Operates on the cached object returned by the lookup operation\n- Examples:\n  - \"$.name\" - Extract the name field from the top level\n  - \"$.details.price\" - Extract a nested price field\n  - \"$.attributes[0].value\" - Extract a value from the first element of an array\n\n**Usage scenario**\n\nWhen a lookup cache contains complex objects:\n```json\n// Cache entry for key \"PROD-123\":\n{\n  \"id\": \"PROD-123\",\n  \"name\": \"Premium Widget\",\n  \"details\": {\n    \"price\": 99.99,\n    \"currency\": \"USD\",\n    \"inStock\": true\n  }\n}\n```\n\nSetting extract to \"$.details.price\" would return 99.99 as the lookup result.\n\nIf no extract is provided, the entire cached object is returned as the lookup result.\n"},"default":{"type":["string","null"],"description":"Default value to use when the source value is not found in the lookup map.\nThe platform stores `null` here when no default is configured.\n\nThis value is used as a fallback when:\n1. The source value doesn't match any key in the map\n2. allowFailures is true or omitted\n\nSetting an appropriate default helps prevent flow failures due to unexpected\nvalues and provides predictable behavior for edge cases.\n\nCommon default patterns include:\n- Descriptive unknowns: \"Unknown Country\", \"Unspecified Status\"\n- Original value indicators: \"{Original Value}\", \"No mapping found\"\n- Neutral values: \"Other\", \"N/A\", \"Miscellaneous\"\n\nA save refuses a default together with an explicit `allowFailures: false`\n(422 `Lookups with default set can not have allowFailures explicitly set to\nfalse.`): a lookup either fails a miss (`allowFailures: false`, no default) or\nfalls back to its default.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, missing lookup values will use the default value rather than causing an error.\n\n**Behavior control**\n\nThis field determines how the system handles source values that don't exist in the map:\n\n- true: Use the default value for missing mappings and continue processing\n- false: Treat missing mappings as errors, failing the record (`lookup_error` for a\n  static map, `lookupcache_evaluation_failed` for a lookup cache)\n- omitted or null: the record doesn't fail either. On an import a static lookup writes\n  its default if it has one, and a static or cache lookup without one leaves the\n  mapped field out of the output, with no error\n\n**Recommendation**\n\nSet this to true when:\n- New source values might appear over time\n- Data quality issues could introduce unexpected values\n- Processing should continue even with imperfect mapping\n\nSet this to false when:\n- Complete data accuracy is critical\n- All possible source values are known and controlled\n- Missing mappings indicate serious data problems that should be addressed\n\nA default raises no error: records that miss go out with the default value and\nnothing reports them. Use true only when that fallback is acceptable at the\ndestination, and set false explicitly when a missing value must stop the record.\n"}}}},"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: one in the script named by `_scriptId`, or one the code deployed on\nthe stack named by `_stackId` exposes."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`. Send this or `_stackId`, not both.\nMust be a script in this account: any other id fails the save with 422, naming the hook,\nsuch as \"Only a script owned by the user or a script shared by the Integration App can\nbe used at hooks.preMap._scriptId.\" on an import's `preMap`."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments.\nThe API doesn't enforce the either/or: a save carrying both `_scriptId` and `_stackId`\nanswers 200 and stores both, and the script and the stack then each list the step as a\ndependent."},"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."}}},"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"},"AiAgent":{"type":"object","required":["_id","name","adaptorType","aiAgent","createdAt","lastModified"],"description":"AI agent import object as returned by the API.","allOf":[{"$ref":"#/components/schemas/AiAgentBase"},{"$ref":"#/components/schemas/ResourceResponse"},{"type":"object","properties":{"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Export that feeds data into this import within a flow."},"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"apiIdentifier":{"type":"string","readOnly":true,"description":"Ten-character hex identifier for the resource."},"lookups":{"type":"array","readOnly":true,"description":"Lookup configurations associated with this import.","items":{"type":"object"}},"_templateId":{"type":"string","format":"objectId","readOnly":true,"description":"Template this AI agent was created from."},"draftExpiresAt":{"type":"string","format":"date-time","readOnly":true,"description":"When the draft version of this AI agent expires. Drafts are staged by Celigo's\nAI assistant when it proposes changes for review; this field is present only\nwhile an unapproved draft is pending."},"debugUntil":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp until which verbose debug logging runs for this AI agent. Read-only\non create/update; enable it through the debug PATCH endpoint, not a full update."}}}]},"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"]},"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"}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"400-bad-request":{"description":"Bad request. The server could not understand the request because of malformed syntax or invalid parameters.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"422-unprocessable-entity":{"description":"Unprocessable entity. The request was well-formed but was unable to be followed due to semantic errors.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/imports":{"post":{"summary":"Create an AI agent","description":"Creates a new AI agent import. The provider decides the configuration\nformat: `aiAgent.openai` for OpenAI, `aiAgent.litellm` for Gemini and\nAnthropic Claude, or the generic flat format (`aiAgent.model`,\n`aiAgent.modelOptions`, …) for the catalog providers Mistral, xAI,\nHugging Face, DeepSeek, Cohere, and Groq — a block that does not match the\nprovider is rejected. For a built-in provider, omit `_connectionId` to\nuse platform-managed credentials or include it for BYOK; a catalog\nprovider needs a BYOK connection to save — without one the request\nfails with 422 `connection_required` on `_connectionId`.","operationId":"createAiAgent","tags":["AI Agents"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Request"}}}},"responses":{"201":{"description":"Created AI agent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiAgent"}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
````

## Get an AI agent

> Returns the full configuration of a single AI agent import.

````json
{"openapi":"3.2.0","info":{"title":"AI Agents","version":"1.0.0"},"tags":[{"name":"AI Agents","description":"AI agents are LLM-powered import steps that classify, extract, validate, or\ngenerate data within integration flows. They are stored as import resources\nwith `adaptorType: AiAgentImport` and managed via the `/v1/imports` endpoints.\n\nThree built-in providers — OpenAI (via the Responses API), Google Gemini (via\nLiteLLM), and Anthropic Claude (via LiteLLM) — run on platform-managed\ncredentials or, with a `_connectionId`, on your own key (BYOK). Catalog\nproviders — Mistral, xAI, Hugging Face, DeepSeek, Cohere, and Groq — use one generic\nflat configuration format and always run on a BYOK connection.\n\n## AI agent schema\n\n{% openapi-schemas spec=\"ai-agent\" schemas=\"AiAgent\" 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":{"AiAgent":{"type":"object","required":["_id","name","adaptorType","aiAgent","createdAt","lastModified"],"description":"AI agent import object as returned by the API.","allOf":[{"$ref":"#/components/schemas/AiAgentBase"},{"$ref":"#/components/schemas/ResourceResponse"},{"type":"object","properties":{"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Export that feeds data into this import within a flow."},"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"apiIdentifier":{"type":"string","readOnly":true,"description":"Ten-character hex identifier for the resource."},"lookups":{"type":"array","readOnly":true,"description":"Lookup configurations associated with this import.","items":{"type":"object"}},"_templateId":{"type":"string","format":"objectId","readOnly":true,"description":"Template this AI agent was created from."},"draftExpiresAt":{"type":"string","format":"date-time","readOnly":true,"description":"When the draft version of this AI agent expires. Drafts are staged by Celigo's\nAI assistant when it proposes changes for review; this field is present only\nwhile an unapproved draft is pending."},"debugUntil":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp until which verbose debug logging runs for this AI agent. Read-only\non create/update; enable it through the debug PATCH endpoint, not a full update."}}}]},"AiAgentBase":{"type":"object","description":"Writable AI agent import fields shared by the request and response schemas. Set\n`aiAgent.provider` to choose the LLM backend, then configure the matching format:\n`aiAgent.openai` for OpenAI, `aiAgent.litellm` for Gemini and Anthropic Claude\n(with provider-specific settings under `aiAgent.litellm._overrides`), or the\ngeneric flat format (`aiAgent.model`, `aiAgent.modelOptions`, …) for the catalog\nproviders Mistral, xAI, Hugging Face, DeepSeek, Cohere, and Groq.","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Display name for the AI agent."},"description":{"type":"string","description":"Optional description of the AI agent's purpose."},"_connectionId":{"type":"string","format":"objectId","description":"Connection for BYOK (bring your own key) — an HTTP connection to the vendor's API.\nOptional for the built-in providers (`openai`, `gemini`, `anthropic`), which run on\nplatform-managed credentials when it is omitted. Catalog providers (`mistral`, `xai`,\n`huggingface`, `deepseek`, `cohere`, `groq`) have no platform-managed credentials: the save\nrequires it — without one the agent fails to save with 422 `connection_required` on\n`_connectionId`."},"_integrationId":{"type":"string","format":"objectId","description":"Integration this AI agent belongs to. Omitted for a standalone agent."},"adaptorType":{"type":"string","enum":["AiAgentImport"],"description":"Must be `AiAgentImport`."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this AI agent was created from, set when it is part of an installed integration app."},"aiAgent":{"$ref":"#/components/schemas/AiAgentConfig"},"mappings":{"allOf":[{"$ref":"#/components/schemas/Mappings"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Selects the data context that `mappings[].extract` evaluates against. With no\nmappings the agent receives the entire record as JSON (or only the record's own\ntop-level `text`, `record` or `files` fields, when it has any) and `inputContext`\nis moot. `record`\n(the default) extracts against the flat record; `envelope` extracts against the\nruntime envelope, which exposes the record alongside job, settings, and other\nrun-time metadata (`$.record.field`, `$.settings.field`, etc.). Same contract as\nthe import resource's `inputContext`."},"filter":{"description":"Filter applied to incoming records before they are sent to the LLM. Records that match\ncontinue through the agent; records that don't skip it, counted as ignored with no model\ncall, and continue to the next step.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"responseTransform":{"description":"Transformation that reshapes the LLM's response after records are processed, before the\nresponse is handled by response mappings and downstream steps.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the agent's import lifecycle.\nAI agents do not aggregate records, so the `postAggregate` hook that other import\ntypes expose is intentionally omitted here.","properties":{"preMap":{"description":"Hook that runs on each page of records before the agent's mappings are applied.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postMap":{"description":"Hook that runs after mappings are applied, before records are sent to the LLM.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postSubmit":{"description":"Hook that runs after the LLM responds, with both the submitted records and the responses.","allOf":[{"$ref":"#/components/schemas/Hook"}]}}},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"distributed":{"type":"boolean","description":"Always false for AI agent imports — the agent executes on Celigo's servers, not inside a target application."},"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 are silently skipped. Rarely meaningful for AI agents; accepted for import-surface parity."},"ignoreMissing":{"type":"boolean","description":"When true, records that do not already exist in the destination are silently skipped. Rarely meaningful for AI agents; accepted for import-surface parity."},"mockResponse":{"type":"array","description":"Predefined response records used in place of calling the LLM when testing the agent,\nso flows can run without spending tokens. Records must be in integrator.io canonical\nformat — 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 for structured-output agents."},"_text":{"type":"string","description":"Mock response body for text-output agents."},"_headers":{"type":"object","description":"Mock response headers."},"blobKey":{"type":"string","description":"Mock output of an agent with `blob` output (image generation:\n`openai.output.format.type` or `litellm.responseFormat.type` set to `blob`),\nwhich returns the stored image's key here. Unrelated to the import-level `blob`\nflag, which agents refuse."}}}}}},"AiAgentConfig":{"type":"object","description":"AI agent configuration for an AiAgentImport. An `ai_agent` guardrail is configured by\nthe guardrail spec's `GuardrailAiAgentConfig` instead, which has no `tools` arrays.\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 the\nsave requires one — without it the agent fails to save with 422\n`connection_required` on `_connectionId`.\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","max"],"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":"Caps the length of the model's response, in tokens. An agent created without it is\nsaved with 5000; raise it for long outputs such as large extractions."},"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":"Which editor the UI opens `jsonSchema` in; the schema sent to the provider\nis the same either way, and the field is only meaningful when `type` is\n\"json_schema\". Unset opens the visual editor, and an edit there rebuilds\n`jsonSchema` from the field list, keeping only `type`, `properties`,\n`required`, `items`, `description`, `enum`, `pattern`, length and numeric\nbounds, `multipleOf`, `minItems`/`maxItems` and a `\"null\"` type — other\nkeywords are dropped without a prompt, and `integer` becomes `number`."},"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`."}}},"schema":{"type":"object","readOnly":true,"additionalProperties":true,"description":"Server-written copy of `jsonSchema`, returned whenever `jsonSchema` is set — on\nAI agent imports and on guardrail agents alike. Send `jsonSchema`, not this field."}},"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","code_execution","tool"],"description":"Type of tool. Every type except `code_execution` is configured through the\nsibling object of the same name; OpenAI spells this one `code_execution`, while\nthe Gemini and Anthropic overrides spell theirs `codeExecution` — the other spelling\nfails with 422 `enum`."},"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"]}}}},"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":"Which editor the UI opens `jsonSchema` in; the schema sent to the provider is\nthe same either way, and the field is only meaningful when `type` is\n\"json_schema\". Unset opens the visual editor, and an edit there rebuilds\n`jsonSchema` from the field list, keeping only `type`, `properties`, `required`,\n`items`, `description`, `enum`, `pattern`, length and numeric bounds,\n`multipleOf`, `minItems`/`maxItems` and a `\"null\"` type — other keywords are\ndropped without a prompt, and `integer` becomes `number`."},"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","codeExecution","mcp","tool"],"description":"Type of Gemini tool. `codeExecution` is the Gemini spelling — `code_execution`\nfails with 422 `enum`."},"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"]}}}},"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","codeExecution"],"description":"Type of Claude tool. `codeExecution` is the Anthropic spelling — `code_execution`\nfails with 422 `enum`."},"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). Each\nentry is either a plain tool name or an object carrying display\nmetadata — same contract as the OpenAI and Gemini `allowedTools`.","items":{"oneOf":[{"title":"name","type":"string","description":"Tool name as exposed by the MCP server."},{"title":"tool","type":"object","required":["name"],"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."}}}]}}}},"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."}}}}}}}}},"Mappings":{"type":"array","description":"Array of field mapping configurations for transforming data from one format into another.\n\n**Guidance**\n\nThis schema is designed around RECURSION as its core architectural principle. Understanding this recursive\nnature is essential for building effective mappings:\n\n1. The schema is self-referential by design - a mapping can contain nested mappings of the same structure\n2. Complex data structures (nested objects and arrays of objects, to any depth) are ALL\n   handled through this recursive pattern; an array of arrays is not (build one in a script hook)\n3. Each mapping handles one level of the data structure; deeper levels are handled by nested mappings\n\nWhen generating mappings programmatically:\n- For simple fields (string, number, boolean): Create single mapping objects\n- For objects: Create a parent mapping with nested 'mappings' array containing child field mappings\n- For arrays: Use 'buildArrayHelper' with extract paths defining array inputs and\n  recursive 'mappings' to define object structures\n\nThe system will process these nested structures recursively during runtime, ensuring proper construction\nof complex hierarchical data while maintaining excellent performance.\n\nOn save, a leaf row with neither a source (`extract`, `hardCodedValue`, `lookupName`) nor a\ndestination (`generate`) is dropped silently, at every nesting level, and so is an object or\nobject-array row left with no source, no destination and no remaining child `mappings` or\n`buildArrayHelper` entries; a row with only one of the two, and a source-less, destination-less\nwrapper that still has children, are kept.\n\nThe shape of the array is the mapper's **JSON structure**. Named top-level items produce an object\nrecord (`record { }`). Exactly one top-level item with `dataType: objectarray` and no `generate` is\nthe `rows [ ]` structure: the mapped record is an array — usually one element per element of the\nsource array that root's `buildArrayHelper` iterates — Handlebars templates such as `http.body`\nbind it as `rows` instead of `record`, and the mapping editor shows that row's destination disabled.\n","items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}}},"items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}},"Filter":{"type":"object","description":"Configuration for selectively processing records based on specified criteria. This object enables\nprecise control over which items are included or excluded from processing operations.\n\n**Filter behavior**\n\nWhen configured, the filter is applied before processing begins:\n- Items that match the filter criteria are processed\n- Items that don't match are completely skipped\n- No partial processing is performed\n\nSend the whole object. A rules array sent alone as the `filter` value is not\nstored, and a full update of an export that carries one removes the filter the\nexport had, with no error.\n\n**Implementation approaches**\n\nThere are two distinct filtering mechanisms available:\n\n**Rule-Based Filtering (`type: \"expression\"`)**\n- **Best For**: Common filtering patterns based on standard attributes\n- **Capabilities**: Filter by names, values, dates, numerical ranges, text patterns\n- **Advantages**: Declarative, no coding required, consistent performance\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear, static criteria for selection\n\n**Script-Based Filtering (`type: \"script\"`)**\n- **Best For**: Complex logic, dynamic criteria, or business rules\n- **Capabilities**: Full programmatic control, access to complete metadata\n- **Advantages**: Maximum flexibility, can implement any filtering logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Simple rules aren't sufficient or logic needs to be dynamic\n","properties":{"type":{"type":"string","description":"Determines which filtering mechanism to use. This choice affects which properties\nmust be configured and how filtering logic is implemented.\n\n**Available types**\n\n**Rule-Based Filtering (`\"expression\"`)**\n- **Required Config**: The `expression` object with rule definitions\n- **Behavior**: Evaluates declarative rules against item attributes\n- **Best For**: Common patterns like name matching, date ranges, value limits\n- **Advantages**: Simpler to configure, no custom code required\n\n**Script-Based Filtering (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to determine which items to process\n- **Best For**: Complex conditions, business logic, dynamic criteria\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard filtering needs (name, size, date), use `\"expression\"`\n2. For complex logic or conditions not covered by expressions, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based filtering. This object enables filtering\nitems based on common attributes without requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define filtering rules that\ncan match against item attributes like name, type, value, date, and other properties.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Rules can be combined with AND/OR logic\n- Each rule can check a specific attribute\n- Multiple conditions can be applied (ranges, pattern matching, exact matches)\n\n**Common filter patterns**\n\n1. **Pattern matching**: Regular expressions with `matches`\n2. **Value range filtering**: Numbers between min and max values\n3. **Date range filtering**: Items created/modified within specific time ranges\n4. **Status checking**: Items with specific status values or properties\n\nFor AI agents: Rule-based filtering should be your first choice when the filtering criteria\ncan be expressed in terms of standard attributes. Only use script-based filtering when\nmore complex logic is required.\n","properties":{"version":{"type":"string","description":"Version identifier for the expression format. Currently only version \"1\" is supported.\n\nThis field ensures future compatibility if the expression format evolves. Always set to \"1\"\nfor current implementations.\n","enum":["1"]},"rules":{"type":"array","description":"Expression array defining filter conditions using prefix notation. The first element is the operator,\nfollowed by its operands which may themselves be nested expression arrays.\n\nThe rule expression follows this pattern:\n- First element: Operator name (string)\n- Remaining elements: Operands for that operator (values or nested expressions)\n\n**Expression structure**\n\nFilter expressions use a prefix notation where operators appear before their operands:\n```\n[operator, operand1, operand2, ...]\n```\n\n**Comparison Operators**\n- `\"equals\"`: Exact match (equals)\n- `\"notequals\"`: Not equal to value (not equals)\n- `\"greaterthan\"`: Value is greater than specified value (is greater than)\n- `\"greaterthanequals\"`: Value is greater than or equal to specified value (is greater than or equals)\n- `\"lessthan\"`: Value is less than specified value (is less than)\n- `\"lessthanequals\"`: Value is less than or equal to specified value (is less than or equals)\n- `\"startswith\"`: String starts with specified prefix (starts with)\n- `\"notstartswith\"`: String does not start with specified prefix (does not start with)\n- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"notendswith\"`: String does not end with specified suffix (does not end with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"empty\"`: Field is missing or empty (is empty)\n- `\"notempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches a regular expression, ignoring case (matches)\n\nOperator names are case-insensitive: `NOTEMPTY` and `endsWith` work and are stored\nas sent. `isempty`, `isnotempty`, `anyof`, `notcontains`, `doesnotstartwith`,\n`doesnotendwith`, `notmatches`, `doesnotmatch` and `notequal` are not operators:\nthey are refused with 422 \"Invalid operation: <name>\", with the name in lowercase,\nboth when the filter is saved and by `POST /v1/processors/filter`. To negate a\ncondition, wrap it in `not`.\n\nThe string comparisons (`equals`, `contains`, `startswith`, `endswith` and their\nnegations) are case-sensitive; wrap the field in `lowercase` to compare without case.\n`matches` is not: every pattern is compiled with the flags `gi`, so `^paid$` also\nmatches `PAID` and `[a-z]` matches capital letters. An inline flag such as `(?i)`\nmakes the pattern invalid. The filter still saves, but in a run every record fails\nwith `FILTER_EVALUATION_FAILED` (\"Invalid regular expression: /(?i)…/gi: Invalid\ngroup\"), and `POST /v1/processors/filter` refuses the call with 422 (live-verified\n2026-10-22).\n\n**Logical Operators**\n- `\"and\"`: All conditions must be true\n- `\"or\"`: At least one condition must be true\n- `\"not\"`: Negates the condition\n\n**Field Access and Type Conversion**\n- `\"extract\"`: Access a field from the item by name\n- `\"settings\"`: Access a custom setting from the flow, flow step, or integration configuration\n- `\"boolean\"`: Convert value to Boolean type\n- `\"epochtime\"`: Convert value to Epoch Time (Unix timestamp)\n- `\"number\"`: Convert value to Number type\n- `\"string\"`: Convert value to String type\n\n**Field Access Details**\n\n**Using `extract` to access record fields:**\n- Retrieves values from the current record being processed\n- Can access nested properties using dot notation (e.g., `\"customer.email\"`)\n- Paths are relative to the record itself: `[\"extract\", \"record.status\"]`\n  matches nothing\n- In a record filter, must be wrapped in a type conversion (`string`, `number`,\n  `boolean` or `epochtime`): a bare `[\"extract\", ...]` operand is refused with 422\n  \"Field operation extract must be called by a Type Conversion Operation but was called\n  by <operator>\", both when an import's filter is saved and by\n  `POST /v1/processors/filter`\n\n**Using `settings` to access configuration values:**\n- Retrieves values from the integration's configuration settings\n- Supports different scopes with prefix notation:\n  - `flow.settingName`: Access flow-level settings\n  - `flowGrouping.settingName`: Access the flow group's settings\n  - `export.settingName`: Access the export's (or lookup's) own settings\n  - `import.settingName`: Access the import's own settings\n  - `integration.settingName`: Access integration-level settings\n  - `connection.settingName`, `iClient.settingName`: Access the step's\n    connection and iClient settings\n- A scope whose resource has no settings can be absent; a rule that reads\n  it matches nothing\n- Must be wrapped in a type conversion like `extract`: a bare\n  `[\"settings\", ...]` operand is refused with 422 \"Field operation settings\n  must be called by a Type Conversion Operation\"\n- Useful for dynamic filtering based on configuration\n\n**Field Transformations**\n- `\"lowercase\"`: Convert string to lowercase\n- `\"uppercase\"`: Convert string to uppercase\n- `\"ceiling\"`: Round number up to the nearest integer\n- `\"floor\"`: Round number down to the nearest integer\n- `\"abs\"`: Get absolute value of a number\n\n**Arithmetic** (number operands)\n- `\"add\"`, `\"subtract\"`, `\"multiply\"`, `\"divide\"`, `\"modulo\"`: for example\n  `[\"subtract\", [\"number\", [\"extract\", \"total\"]], [\"number\", [\"extract\", \"discount\"]]]`.\n  `mod` is not an operator (422 \"Invalid operation: mod.\").\n\n**Dates**\n- `epochtime` reads ISO 8601 strings and epoch-millisecond numbers. An empty\n  string, a string that isn't a date, or epoch milliseconds sent as a string\n  fail it with \"Invalid date. Cannot convert given string or number to date\n  time.\", followed by `; record=` and the record as JSON: in a run that record fails (`FILTER_EVALUATION_FAILED`, source\n  `output_filter` on an export), and `POST /v1/processors/filter` and an\n  export preview refuse the whole call with 422. `and` and `or` evaluate\n  every operand, so a `notempty` test beside it doesn't prevent the failure.\n  A missing or null field doesn't match.\n- `datetime`, `date` and `timestamp` are not type conversions: 422\n  \"Invalid operation: <name>.\"\n- `number` reads a missing or empty field as 0.\n\n**Handlebars operands**\n- An operand can also be a string holding exactly one Handlebars expression, such as\n  `\"{{record.status}}\"` or `\"{{lowercase record.code}}\"`, on either side of the\n  operator. It renders against the record (`record.<field>`; an unprefixed\n  `{{status}}` renders empty) before the comparison.\n- The rendered value is a string: wrap it in `[\"number\", ...]` to compare it with a\n  number, or the rule is refused with 422.\n- `epochtime` does not render Handlebars: `[\"epochtime\", \"{{timestamp}}\"]` is refused\n  with 422 \"invalid datetime format\".\n- A string with text around the expression, or with more than one expression, is\n  compared as a literal.\n- `[\"number\", \"{{timestamp \\\"x\\\"}}\"]` is the current time in epoch milliseconds,\n  so `[\"greaterthanequals\", [\"epochtime\", [\"extract\", \"created\"]], [\"subtract\",\n  [\"number\", \"{{timestamp \\\"x\\\"}}\"], 604800000]]` keeps records created in the\n  last 7 days.\n- An unknown helper fails the rule with 422 \"Failed to evaluate handlebars\n  expression\".\n\nChoose the type conversion that matches the comparison — `number` for numeric\ncomparisons, `epochtime` for dates, `string` for text. For example:\n\n```json\n[\n  \"equals\",\n  [\n    \"number\",  // Convert to number before comparison\n    [\n      \"extract\",\n      \"quantity\"\n    ]\n  ],\n  100\n]\n```\n\nExample with datetime conversion:\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",  // Convert to Unix timestamp before comparison\n    [\n      \"extract\",\n      \"createdDate\"\n    ]\n  ],\n  1609459200000  // January 1, 2021 as Unix timestamp in milliseconds\n]\n```\n\nExample with transformations:\n```json\n[\n  \"and\",\n  [\n    \"matches\",\n    [\n      \"lowercase\",  // Convert to lowercase before matching\n      [\n        \"string\",\n        [\n          \"extract\",\n          \"categories\"\n        ]\n      ]\n    ],\n    \"netsuite\"\n  ],\n  [\n    \"notequals\",\n    [\n      \"string\",\n      [\n        \"extract\",\n        \"recurrence.pattern.type\"\n      ]\n    ],\n    \"\"\n  ]\n]\n```\n\nExample comparing a record field with a flow setting:\n```json\n[\n  \"equals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"trantype\"\n    ]\n  ],\n  [\n    \"string\",\n    [\n      \"settings\",\n      \"flow.trantype\"\n    ]\n  ]\n]\n```\n\n**Examples**\n\nExample 1: Status field is not equal to \"cancelled\"\n```json\n[\n  \"notequals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"status\"\n    ]\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"filename\"\n    ]\n  ],\n  \"HC\"\n]\n```\n\nExample 3: Amount is greater than 100\n```json\n[\n  \"greaterthan\",\n  [\n    \"number\",\n    [\n      \"extract\",\n      \"amount\"\n    ]\n  ],\n  100\n]\n```\n\nExample 4: Order date is after January 1, 2023 (UTC). Compare dates as `epochtime`\nagainst epoch milliseconds: a `string` comparison of ISO timestamps orders values with\ndifferent UTC offsets wrongly.\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",\n    [\n      \"extract\",\n      \"orderDate\"\n    ]\n  ],\n  1672531200000\n]\n```\n","items":{"oneOf":[{"title":"String","type":"string"},{"title":"Number","type":"number"},{"title":"Boolean","type":"boolean"},{"title":"Object","type":"object"},{"title":"Array","type":"array"},{"title":"Null","type":"null"}]}}}},"script":{"type":"object","description":"Configuration for programmable script-based filtering. This object enables complex, custom\nfiltering logic beyond what expression-based filtering can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to determine which items\nshould be processed.\n\n**Implementation approach**\n\nScript-based filtering works by:\n1. Executing the specified function from the referenced script\n2. Passing item data to the function\n3. Using the function's return value (true/false) to determine inclusion\n\n**Common use cases**\n\nScript filtering is ideal for:\n- Complex business logic that can't be expressed as simple rules\n- Dynamic filtering criteria that change based on external factors\n- Content-based filtering that requires deep inspection\n- Advanced pattern matching beyond simple wildcards\n- Multi-stage filtering with intermediate logic\n\nFor AI agents: Only use script-based filtering when expression-based filtering is insufficient.\nScript filtering requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to the Script resource that contains the filtering logic. This must be a valid\nObjectId of a Script resource in this account: any other id fails the save with 422\n(\"Only a script owned by the user or a script shared by the Integration App can be used\nat filter.script._scriptId.\").\n\nThe referenced script must contain the function specified in the `function` field\nand must be written to handle filtering specifically. The script receives\nitem data as its input and must return a boolean value indicating whether\nto process the item (true) or skip it (false).\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n"},"function":{"type":"string","description":"Name of the function within the script to execute for filtering decisions. This function\nmust exist in the script referenced by _scriptId.\n\n**Function requirements**\n\nThe specified function must:\n- Accept item data as its first parameter\n- Return a boolean value (true to process the item, false to skip it)\n- Handle errors gracefully\n- Execute efficiently (as it may run for many items)\n\n**Function signature**\n\n```javascript\nfunction filterItems(itemData) {\n  // itemData contains properties of the item being evaluated\n  // Custom logic here\n  return true; // or false to skip the item\n}\n```\n\nFor AI agents: Ensure the function name exactly matches a function defined in the\nreferenced script, as mismatches will cause the filter to fail.\n"}}}}},"Transform":{"type":"object","description":"Configuration for transforming data during processing operations. This object enables\nreshaping of records.\n\n**Transformation capabilities**\n\nCeligo's transformation engine offers powerful features for data manipulation:\n- Precise field mapping with JSONPath expressions\n- Support for any level of nested arrays\n- Formula-based field value generation\n- Dynamic references to flow and integration settings\n\n**Implementation approaches**\n\nThere are two distinct transformation mechanisms available:\n\n**Rule-Based Transformation (`type: \"expression\"`)**\n- **Best For**: Most transformation scenarios from simple to complex\n- **Capabilities**: Field mapping, formula calculations, lookups, nested data handling\n- **Advantages**: Visual configuration, no coding required, intuitive interface\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear mapping requirements or need to reshape data structure\n\n**Script-Based Transformation (`type: \"script\"`)**\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Capabilities**: Full programmatic control, custom processing, complex business rules\n- **Advantages**: Maximum flexibility, can implement any transformation logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Visual transformation tools aren't sufficient for your use case\n","properties":{"type":{"type":"string","description":"Determines which transformation mechanism to use. This choice affects which properties\nmust be configured and how transformation logic is implemented.\n\n**Available types**\n\n**Rule-Based Transformation (`\"expression\"`)**\n- **Required Config**: The `expression` object with mapping definitions\n- **Behavior**: Applies declarative rules to reshape data\n- **Best For**: Most transformation scenarios from simple to complex\n- **Advantages**: Visual configuration, no coding required\n\n**Script-Based Transformation (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to transform data\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard data transformations, use `\"expression\"`\n2. For complex logic or specialized processing, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based transformations. This object enables reshaping data\nwithout requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define transformation rules that\ncan map, modify, and generate data elements.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Field mappings define how input data is transformed to target fields\n- Formulas can be used to calculate or generate new values\n- Lookups can enrich data by fetching related information\n- Mode determines how records are processed (create new or modify existing)\n","properties":{"version":{"type":"string","description":"Version of the expression format. Determines which rules\nproperty contains the transformation logic.\n","enum":["1","2"]},"rules":{"type":"array","description":"Transformation rules for version 1 expressions. An array of\nrule groups; each group is an array of field-mapping objects.\nMost transforms have a single group. Present when `version`\nis `\"1\"`. The output record contains ONLY the generated\nfields — every unmapped field is dropped (v1 has no\nequivalent of Transform 2.0's `modify` mode), and the\nrecord's trace key does not survive the rebuild.\n","items":{"type":"array","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path to read from. Supports multiple\nsyntaxes: bare field names (`id`), dot notation\n(`fulfillment.shipment_id`), slash-prefixed paths\nfor XML (`/FeedProcessingStatus`), wildcards (`*.id`,\n`*.[Internal ID]`), and array indexing (`SDF[0]`).\n"},"generate":{"type":"string","description":"Target field name to write to. Typically a bare name\n(`id`) or dot path (`SDF.Filter.ID`).\n"},"key":{"type":"string","description":"Auto-generated identifier for this rule, used by the\nUI to track individual rules for editing and reordering.\n"}},"required":["extract","generate"]}}},"rulesTwoDotZero":{"type":"object","description":"Configuration for version 2 transformation rules. This object contains the core logic\nfor how data is mapped, enriched, and transformed.\n\n**Capabilities**\n\nTransformation 2.0 provides:\n- Precise field mapping with JSONPath expressions\n- Support for deeply nested data structures\n- Formula-based field generation\n- Dynamic lookups for data enrichment\n- Multiple operating modes to fit different scenarios\n","properties":{"mode":{"type":"string","description":"Transformation mode that determines how records are handled during processing.\n\n**Available modes**\n\n**Create Mode (`\"create\"`)**\n- **Behavior**: Builds entirely new output records from inputs\n- **Use When**: Output structure differs significantly from input\n- **Advantage**: Clean slate approach, no field inheritance\n\n**Modify Mode (`\"modify\"`)**\n- **Behavior**: Makes targeted edits to existing records\n- **Use When**: Output structure should remain similar to input\n- **Advantage**: Preserves unmapped fields from the original record\n","enum":["create","modify"]},"mappings":{"$ref":"#/components/schemas/Mappings"},"lookups":{"allOf":[{"description":"Shared lookup tables used across all mappings defined in the transformation rules.\n\n**Purpose**\n\nLookups provide centralized value translation that can be referenced from any mapping\nin your transformation configuration. They enable consistent translation of codes, IDs,\nand values between systems without duplicating translation logic.\n\n**Usage in transformations**\n\nLookups are particularly valuable in transformations for:\n\n- **Data Normalization**: Standardizing values from diverse source systems\n- **Code Translation**: Converting between different coding systems (e.g., status codes)\n- **Field Enrichment**: Adding descriptive values based on ID or code lookups\n- **Cross-Reference Resolution**: Mapping identifiers between integrated systems\n\n**Implementation**\n\nLookups are defined once in this array and referenced by name in mappings:\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"statusMapping\",\n    \"map\": {\n      \"A\": \"Active\",\n      \"I\": \"Inactive\",\n      \"P\": \"Pending\"\n    },\n    \"default\": \"Unknown Status\"\n  }\n]\n```\n\nThen referenced in mappings using the lookupName property:\n\n```json\n{\n  \"generate\": \"status\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.statusCode\",\n  \"lookupName\": \"statusMapping\"\n}\n```\n\nThe system automatically applies the lookup during transformation processing.\n\nFor complete details on lookup properties and behavior, see the Lookups schema.\n"},{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the JSON shape the transformTwoDotZero processor\nevaluates `mappings[].extract` JSONPath values against at\nflow runtime. Applies only to Transform 2.0 (v2,\n`rulesTwoDotZero`); v1 transforms (the `rules` array on\n`transform.expression.rules`) and script-mode transforms\nignore this field.\n"}}}}},"script":{"type":"object","description":"Configuration for programmable script-based transformations. This object enables complex, custom\ntransformation logic beyond what expression-based transformations can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to transform data according to\nspecialized business rules or complex algorithms.\n\n**Implementation approach**\n\nScript-based transformation works by:\n1. Executing the specified function from the referenced script\n2. Passing input data to the function\n3. Using the function's return value as the transformed output\n\n**Common use cases**\n\nScript transformation is ideal for:\n- Complex business logic that can't be expressed through mappings\n- Algorithmic transformations requiring computation\n- Dynamic transformations based on external factors\n- Legacy system data format compatibility\n- Multi-stage processing with intermediate steps\n\nOnly use script-based transformation when expression-based transformation is insufficient.\nScript transformation requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to a predefined script resource containing the transformation logic.\n\nThe referenced script should contain the function specified in the\n'function' property. It must be a script in this account: any other id fails the save\nwith 422 (\"Only a script owned by the user or a script shared by the Integration App can\nbe used at transform.script._scriptId.\").\n","format":"objectid"},"function":{"type":"string","description":"Name of the function within the script to execute for transformation. This function\nmust exist in the script referenced by _scriptId.\n"}}}}},"Lookups":{"type":"array","description":"Configuration for value-to-value transformations using lookup tables.\n\n**Purpose**\n\nLookups provide a way to translate values from one system to another. They transform\ninput values into output values using either static mapping tables or\ndynamic lookup caches.\n\n**Lookup mechanisms**\n\nThere are two distinct lookup mechanisms available:\n\n1. **Static Lookups**: Define a simple key-value map object and store it as part of your resource\n   - Best for: Small, fixed sets of values that rarely change\n   - Implementation: Configure the `map` object with input-to-output value mappings\n   - Example: Country codes, status values, simple translations\n\n2. **Dynamic Lookups**: Reference an existing 'Lookup Cache' resource in your Celigo account\n   - Best for: Large datasets, frequently changing values, or complex reference data\n   - Implementation: Configure `_lookupCacheId` to reference cached data maintained independently\n   - Example: Product catalogs, customer databases, pricing information\n\n**Property usage**\n\nThere are two mutually exclusive ways to configure lookups, depending on which mechanism you choose:\n\n1. **For Static Mappings**: Configure the `map` property with a direct key-value object\n   ```json\n   \"map\": {\"US\": \"United States\", \"CA\": \"Canada\"}\n   ```\n\n2. **For Dynamic Lookups**: Configure the following properties:\n   - `_lookupCacheId`: Reference to the lookup cache resource\n   - `extract`: JSON path to extract specific value from the returned lookup object\n\n**When to use**\n\nLookups are ideal for:\n\n1. **Value Translation**: Mapping codes or IDs to human-readable values\n\n2. **Data Enrichment**: Adding related information to records during processing\n\n3. **Normalization**: Ensuring consistent formatting of values across systems\n\n**Implementation details**\n\nLookups can be referenced in:\n\n1. **Field Mappings**: Direct use in field transformation configurations\n\n2. **Handlebars Templates**: Use within templates with the syntax:\n   ```\n   {{lookup 'lookupName' record.fieldName}}\n   ```\n\n**Example usage**\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"countryCodeToName\",\n    \"map\": {\n      \"US\": \"United States\",\n      \"CA\": \"Canada\",\n      \"UK\": \"United Kingdom\"\n    },\n    \"default\": \"Unknown Country\",\n    \"allowFailures\": true\n  },\n  {\n    \"name\": \"productDetails\",\n    \"_lookupCacheId\": \"60a2c4e6f321d800129a1a3c\",\n    \"extract\": \"$.details.price\",\n    \"allowFailures\": false\n  }\n]\n```\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for the lookup table within this configuration.\n\nThis name must be unique within the scope where the lookup is defined and is used to reference\nthe lookup in handlebars templates with the syntax {{lookup 'name' value}}.\n\nChoose descriptive names that indicate the transformation purpose, such as:\n- \"countryCodeToName\" for country code to full name conversion\n- \"statusMapping\" for status code translations\n- \"departmentCodes\" for department code to name mapping\n"},"map":{"type":["object","null"],"description":"The lookup mapping table as key-value pairs. The platform stores `null`\nhere on dynamic lookups, which resolve values at runtime instead of\nfrom a static table.\n\nThis object contains the input values as keys and their corresponding\noutput values. When a input value matches a key in this object,\nit will be replaced with the corresponding value.\n\nThe map should be kept to a reasonable size (typically under 100 entries)\nfor optimal performance. For larger mapping requirements, consider using\ndynamic lookups instead.\n\nMaps can include:\n- Simple code to name conversions: {\"US\": \"United States\"}\n- Status transformations: {\"A\": \"Active\", \"I\": \"Inactive\"}\n- ID to name mappings: {\"100\": \"Marketing\", \"200\": \"Sales\"}\n\nValues can be strings, numbers, or booleans, but all are stored as strings\nin the configuration.\n"},"_lookupCacheId":{"type":"string","description":"Reference to a LookupCache resource that contains the reference data for the lookup.\n\n**Purpose**\n\nThis field connects the lookup to an external data source that has been cached in the system.\nUnlike static lookups that use the `map` property, dynamic lookups can reference large datasets\nor frequently changing information without requiring constant updates to the integration.\n\n**Implementation details**\n\nThe LookupCache resource referenced by this ID contains:\n- The data records to be used as a reference source\n- Configuration for how the data should be indexed and accessed\n- Caching parameters to balance performance with data freshness\n\n**Usage patterns**\n\nCommonly used to reference:\n- Product catalogs or SKU databases\n- Customer or account information\n- Pricing tables or discount rules\n- Complex business logic lookup tables\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n","format":"objectid"},"extract":{"type":"string","description":"JSON path expression that extracts a specific value from the cached lookup object.\n\n**Purpose**\n\nWhen using dynamic lookups with a LookupCache, this JSON path identifies which field to extract\nfrom the cached object after it has been retrieved using the lookup key.\n\n**Implementation details**\n\n- Must use JSON path syntax (similar to mapping extract fields)\n- Operates on the cached object returned by the lookup operation\n- Examples:\n  - \"$.name\" - Extract the name field from the top level\n  - \"$.details.price\" - Extract a nested price field\n  - \"$.attributes[0].value\" - Extract a value from the first element of an array\n\n**Usage scenario**\n\nWhen a lookup cache contains complex objects:\n```json\n// Cache entry for key \"PROD-123\":\n{\n  \"id\": \"PROD-123\",\n  \"name\": \"Premium Widget\",\n  \"details\": {\n    \"price\": 99.99,\n    \"currency\": \"USD\",\n    \"inStock\": true\n  }\n}\n```\n\nSetting extract to \"$.details.price\" would return 99.99 as the lookup result.\n\nIf no extract is provided, the entire cached object is returned as the lookup result.\n"},"default":{"type":["string","null"],"description":"Default value to use when the source value is not found in the lookup map.\nThe platform stores `null` here when no default is configured.\n\nThis value is used as a fallback when:\n1. The source value doesn't match any key in the map\n2. allowFailures is true or omitted\n\nSetting an appropriate default helps prevent flow failures due to unexpected\nvalues and provides predictable behavior for edge cases.\n\nCommon default patterns include:\n- Descriptive unknowns: \"Unknown Country\", \"Unspecified Status\"\n- Original value indicators: \"{Original Value}\", \"No mapping found\"\n- Neutral values: \"Other\", \"N/A\", \"Miscellaneous\"\n\nA save refuses a default together with an explicit `allowFailures: false`\n(422 `Lookups with default set can not have allowFailures explicitly set to\nfalse.`): a lookup either fails a miss (`allowFailures: false`, no default) or\nfalls back to its default.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, missing lookup values will use the default value rather than causing an error.\n\n**Behavior control**\n\nThis field determines how the system handles source values that don't exist in the map:\n\n- true: Use the default value for missing mappings and continue processing\n- false: Treat missing mappings as errors, failing the record (`lookup_error` for a\n  static map, `lookupcache_evaluation_failed` for a lookup cache)\n- omitted or null: the record doesn't fail either. On an import a static lookup writes\n  its default if it has one, and a static or cache lookup without one leaves the\n  mapped field out of the output, with no error\n\n**Recommendation**\n\nSet this to true when:\n- New source values might appear over time\n- Data quality issues could introduce unexpected values\n- Processing should continue even with imperfect mapping\n\nSet this to false when:\n- Complete data accuracy is critical\n- All possible source values are known and controlled\n- Missing mappings indicate serious data problems that should be addressed\n\nA default raises no error: records that miss go out with the default value and\nnothing reports them. Use true only when that fallback is acceptable at the\ndestination, and set false explicitly when a missing value must stop the record.\n"}}}},"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: one in the script named by `_scriptId`, or one the code deployed on\nthe stack named by `_stackId` exposes."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`. Send this or `_stackId`, not both.\nMust be a script in this account: any other id fails the save with 422, naming the hook,\nsuch as \"Only a script owned by the user or a script shared by the Integration App can\nbe used at hooks.preMap._scriptId.\" on an import's `preMap`."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments.\nThe API doesn't enforce the either/or: a save carrying both `_scriptId` and `_stackId`\nanswers 200 and stores both, and the script and the stack then each list the step as a\ndependent."},"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."}}},"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"},"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"]},"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"}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/imports/{_id}":{"get":{"summary":"Get an AI agent","description":"Returns the full configuration of a single AI agent import.","operationId":"getAiAgentById","tags":["AI Agents"],"parameters":[{"name":"_id","in":"path","description":"Import resource ID.","required":true,"schema":{"type":"string","format":"objectId"}}],"responses":{"200":{"description":"AI agent import object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiAgent"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
````

## Update an AI agent

> Replaces the full configuration of an AI agent import.

````json
{"openapi":"3.2.0","info":{"title":"AI Agents","version":"1.0.0"},"tags":[{"name":"AI Agents","description":"AI agents are LLM-powered import steps that classify, extract, validate, or\ngenerate data within integration flows. They are stored as import resources\nwith `adaptorType: AiAgentImport` and managed via the `/v1/imports` endpoints.\n\nThree built-in providers — OpenAI (via the Responses API), Google Gemini (via\nLiteLLM), and Anthropic Claude (via LiteLLM) — run on platform-managed\ncredentials or, with a `_connectionId`, on your own key (BYOK). Catalog\nproviders — Mistral, xAI, Hugging Face, DeepSeek, Cohere, and Groq — use one generic\nflat configuration format and always run on a BYOK connection.\n\n## AI agent schema\n\n{% openapi-schemas spec=\"ai-agent\" schemas=\"AiAgent\" 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":"Configuration for an AI agent import. Set `aiAgent.provider` to choose\nthe LLM backend, then configure the matching format: `aiAgent.openai` for\nOpenAI, `aiAgent.litellm` for Gemini and Anthropic Claude, or the generic\nflat format (`aiAgent.model`, `aiAgent.modelOptions`, …) for the catalog\nproviders. Omit `_connectionId` to use platform-managed credentials on a\nbuilt-in provider; catalog providers need a BYOK connection to run.","required":["name","adaptorType","aiAgent"],"allOf":[{"$ref":"#/components/schemas/AiAgentBase"}]},"AiAgentBase":{"type":"object","description":"Writable AI agent import fields shared by the request and response schemas. Set\n`aiAgent.provider` to choose the LLM backend, then configure the matching format:\n`aiAgent.openai` for OpenAI, `aiAgent.litellm` for Gemini and Anthropic Claude\n(with provider-specific settings under `aiAgent.litellm._overrides`), or the\ngeneric flat format (`aiAgent.model`, `aiAgent.modelOptions`, …) for the catalog\nproviders Mistral, xAI, Hugging Face, DeepSeek, Cohere, and Groq.","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Display name for the AI agent."},"description":{"type":"string","description":"Optional description of the AI agent's purpose."},"_connectionId":{"type":"string","format":"objectId","description":"Connection for BYOK (bring your own key) — an HTTP connection to the vendor's API.\nOptional for the built-in providers (`openai`, `gemini`, `anthropic`), which run on\nplatform-managed credentials when it is omitted. Catalog providers (`mistral`, `xai`,\n`huggingface`, `deepseek`, `cohere`, `groq`) have no platform-managed credentials: the save\nrequires it — without one the agent fails to save with 422 `connection_required` on\n`_connectionId`."},"_integrationId":{"type":"string","format":"objectId","description":"Integration this AI agent belongs to. Omitted for a standalone agent."},"adaptorType":{"type":"string","enum":["AiAgentImport"],"description":"Must be `AiAgentImport`."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this AI agent was created from, set when it is part of an installed integration app."},"aiAgent":{"$ref":"#/components/schemas/AiAgentConfig"},"mappings":{"allOf":[{"$ref":"#/components/schemas/Mappings"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Selects the data context that `mappings[].extract` evaluates against. With no\nmappings the agent receives the entire record as JSON (or only the record's own\ntop-level `text`, `record` or `files` fields, when it has any) and `inputContext`\nis moot. `record`\n(the default) extracts against the flat record; `envelope` extracts against the\nruntime envelope, which exposes the record alongside job, settings, and other\nrun-time metadata (`$.record.field`, `$.settings.field`, etc.). Same contract as\nthe import resource's `inputContext`."},"filter":{"description":"Filter applied to incoming records before they are sent to the LLM. Records that match\ncontinue through the agent; records that don't skip it, counted as ignored with no model\ncall, and continue to the next step.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"responseTransform":{"description":"Transformation that reshapes the LLM's response after records are processed, before the\nresponse is handled by response mappings and downstream steps.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the agent's import lifecycle.\nAI agents do not aggregate records, so the `postAggregate` hook that other import\ntypes expose is intentionally omitted here.","properties":{"preMap":{"description":"Hook that runs on each page of records before the agent's mappings are applied.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postMap":{"description":"Hook that runs after mappings are applied, before records are sent to the LLM.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postSubmit":{"description":"Hook that runs after the LLM responds, with both the submitted records and the responses.","allOf":[{"$ref":"#/components/schemas/Hook"}]}}},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"distributed":{"type":"boolean","description":"Always false for AI agent imports — the agent executes on Celigo's servers, not inside a target application."},"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 are silently skipped. Rarely meaningful for AI agents; accepted for import-surface parity."},"ignoreMissing":{"type":"boolean","description":"When true, records that do not already exist in the destination are silently skipped. Rarely meaningful for AI agents; accepted for import-surface parity."},"mockResponse":{"type":"array","description":"Predefined response records used in place of calling the LLM when testing the agent,\nso flows can run without spending tokens. Records must be in integrator.io canonical\nformat — 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 for structured-output agents."},"_text":{"type":"string","description":"Mock response body for text-output agents."},"_headers":{"type":"object","description":"Mock response headers."},"blobKey":{"type":"string","description":"Mock output of an agent with `blob` output (image generation:\n`openai.output.format.type` or `litellm.responseFormat.type` set to `blob`),\nwhich returns the stored image's key here. Unrelated to the import-level `blob`\nflag, which agents refuse."}}}}}},"AiAgentConfig":{"type":"object","description":"AI agent configuration for an AiAgentImport. An `ai_agent` guardrail is configured by\nthe guardrail spec's `GuardrailAiAgentConfig` instead, which has no `tools` arrays.\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 the\nsave requires one — without it the agent fails to save with 422\n`connection_required` on `_connectionId`.\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","max"],"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":"Caps the length of the model's response, in tokens. An agent created without it is\nsaved with 5000; raise it for long outputs such as large extractions."},"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":"Which editor the UI opens `jsonSchema` in; the schema sent to the provider\nis the same either way, and the field is only meaningful when `type` is\n\"json_schema\". Unset opens the visual editor, and an edit there rebuilds\n`jsonSchema` from the field list, keeping only `type`, `properties`,\n`required`, `items`, `description`, `enum`, `pattern`, length and numeric\nbounds, `multipleOf`, `minItems`/`maxItems` and a `\"null\"` type — other\nkeywords are dropped without a prompt, and `integer` becomes `number`."},"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`."}}},"schema":{"type":"object","readOnly":true,"additionalProperties":true,"description":"Server-written copy of `jsonSchema`, returned whenever `jsonSchema` is set — on\nAI agent imports and on guardrail agents alike. Send `jsonSchema`, not this field."}},"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","code_execution","tool"],"description":"Type of tool. Every type except `code_execution` is configured through the\nsibling object of the same name; OpenAI spells this one `code_execution`, while\nthe Gemini and Anthropic overrides spell theirs `codeExecution` — the other spelling\nfails with 422 `enum`."},"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"]}}}},"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":"Which editor the UI opens `jsonSchema` in; the schema sent to the provider is\nthe same either way, and the field is only meaningful when `type` is\n\"json_schema\". Unset opens the visual editor, and an edit there rebuilds\n`jsonSchema` from the field list, keeping only `type`, `properties`, `required`,\n`items`, `description`, `enum`, `pattern`, length and numeric bounds,\n`multipleOf`, `minItems`/`maxItems` and a `\"null\"` type — other keywords are\ndropped without a prompt, and `integer` becomes `number`."},"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","codeExecution","mcp","tool"],"description":"Type of Gemini tool. `codeExecution` is the Gemini spelling — `code_execution`\nfails with 422 `enum`."},"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"]}}}},"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","codeExecution"],"description":"Type of Claude tool. `codeExecution` is the Anthropic spelling — `code_execution`\nfails with 422 `enum`."},"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). Each\nentry is either a plain tool name or an object carrying display\nmetadata — same contract as the OpenAI and Gemini `allowedTools`.","items":{"oneOf":[{"title":"name","type":"string","description":"Tool name as exposed by the MCP server."},{"title":"tool","type":"object","required":["name"],"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."}}}]}}}},"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."}}}}}}}}},"Mappings":{"type":"array","description":"Array of field mapping configurations for transforming data from one format into another.\n\n**Guidance**\n\nThis schema is designed around RECURSION as its core architectural principle. Understanding this recursive\nnature is essential for building effective mappings:\n\n1. The schema is self-referential by design - a mapping can contain nested mappings of the same structure\n2. Complex data structures (nested objects and arrays of objects, to any depth) are ALL\n   handled through this recursive pattern; an array of arrays is not (build one in a script hook)\n3. Each mapping handles one level of the data structure; deeper levels are handled by nested mappings\n\nWhen generating mappings programmatically:\n- For simple fields (string, number, boolean): Create single mapping objects\n- For objects: Create a parent mapping with nested 'mappings' array containing child field mappings\n- For arrays: Use 'buildArrayHelper' with extract paths defining array inputs and\n  recursive 'mappings' to define object structures\n\nThe system will process these nested structures recursively during runtime, ensuring proper construction\nof complex hierarchical data while maintaining excellent performance.\n\nOn save, a leaf row with neither a source (`extract`, `hardCodedValue`, `lookupName`) nor a\ndestination (`generate`) is dropped silently, at every nesting level, and so is an object or\nobject-array row left with no source, no destination and no remaining child `mappings` or\n`buildArrayHelper` entries; a row with only one of the two, and a source-less, destination-less\nwrapper that still has children, are kept.\n\nThe shape of the array is the mapper's **JSON structure**. Named top-level items produce an object\nrecord (`record { }`). Exactly one top-level item with `dataType: objectarray` and no `generate` is\nthe `rows [ ]` structure: the mapped record is an array — usually one element per element of the\nsource array that root's `buildArrayHelper` iterates — Handlebars templates such as `http.body`\nbind it as `rows` instead of `record`, and the mapping editor shows that row's destination disabled.\n","items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}}},"items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}},"Filter":{"type":"object","description":"Configuration for selectively processing records based on specified criteria. This object enables\nprecise control over which items are included or excluded from processing operations.\n\n**Filter behavior**\n\nWhen configured, the filter is applied before processing begins:\n- Items that match the filter criteria are processed\n- Items that don't match are completely skipped\n- No partial processing is performed\n\nSend the whole object. A rules array sent alone as the `filter` value is not\nstored, and a full update of an export that carries one removes the filter the\nexport had, with no error.\n\n**Implementation approaches**\n\nThere are two distinct filtering mechanisms available:\n\n**Rule-Based Filtering (`type: \"expression\"`)**\n- **Best For**: Common filtering patterns based on standard attributes\n- **Capabilities**: Filter by names, values, dates, numerical ranges, text patterns\n- **Advantages**: Declarative, no coding required, consistent performance\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear, static criteria for selection\n\n**Script-Based Filtering (`type: \"script\"`)**\n- **Best For**: Complex logic, dynamic criteria, or business rules\n- **Capabilities**: Full programmatic control, access to complete metadata\n- **Advantages**: Maximum flexibility, can implement any filtering logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Simple rules aren't sufficient or logic needs to be dynamic\n","properties":{"type":{"type":"string","description":"Determines which filtering mechanism to use. This choice affects which properties\nmust be configured and how filtering logic is implemented.\n\n**Available types**\n\n**Rule-Based Filtering (`\"expression\"`)**\n- **Required Config**: The `expression` object with rule definitions\n- **Behavior**: Evaluates declarative rules against item attributes\n- **Best For**: Common patterns like name matching, date ranges, value limits\n- **Advantages**: Simpler to configure, no custom code required\n\n**Script-Based Filtering (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to determine which items to process\n- **Best For**: Complex conditions, business logic, dynamic criteria\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard filtering needs (name, size, date), use `\"expression\"`\n2. For complex logic or conditions not covered by expressions, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based filtering. This object enables filtering\nitems based on common attributes without requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define filtering rules that\ncan match against item attributes like name, type, value, date, and other properties.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Rules can be combined with AND/OR logic\n- Each rule can check a specific attribute\n- Multiple conditions can be applied (ranges, pattern matching, exact matches)\n\n**Common filter patterns**\n\n1. **Pattern matching**: Regular expressions with `matches`\n2. **Value range filtering**: Numbers between min and max values\n3. **Date range filtering**: Items created/modified within specific time ranges\n4. **Status checking**: Items with specific status values or properties\n\nFor AI agents: Rule-based filtering should be your first choice when the filtering criteria\ncan be expressed in terms of standard attributes. Only use script-based filtering when\nmore complex logic is required.\n","properties":{"version":{"type":"string","description":"Version identifier for the expression format. Currently only version \"1\" is supported.\n\nThis field ensures future compatibility if the expression format evolves. Always set to \"1\"\nfor current implementations.\n","enum":["1"]},"rules":{"type":"array","description":"Expression array defining filter conditions using prefix notation. The first element is the operator,\nfollowed by its operands which may themselves be nested expression arrays.\n\nThe rule expression follows this pattern:\n- First element: Operator name (string)\n- Remaining elements: Operands for that operator (values or nested expressions)\n\n**Expression structure**\n\nFilter expressions use a prefix notation where operators appear before their operands:\n```\n[operator, operand1, operand2, ...]\n```\n\n**Comparison Operators**\n- `\"equals\"`: Exact match (equals)\n- `\"notequals\"`: Not equal to value (not equals)\n- `\"greaterthan\"`: Value is greater than specified value (is greater than)\n- `\"greaterthanequals\"`: Value is greater than or equal to specified value (is greater than or equals)\n- `\"lessthan\"`: Value is less than specified value (is less than)\n- `\"lessthanequals\"`: Value is less than or equal to specified value (is less than or equals)\n- `\"startswith\"`: String starts with specified prefix (starts with)\n- `\"notstartswith\"`: String does not start with specified prefix (does not start with)\n- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"notendswith\"`: String does not end with specified suffix (does not end with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"empty\"`: Field is missing or empty (is empty)\n- `\"notempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches a regular expression, ignoring case (matches)\n\nOperator names are case-insensitive: `NOTEMPTY` and `endsWith` work and are stored\nas sent. `isempty`, `isnotempty`, `anyof`, `notcontains`, `doesnotstartwith`,\n`doesnotendwith`, `notmatches`, `doesnotmatch` and `notequal` are not operators:\nthey are refused with 422 \"Invalid operation: <name>\", with the name in lowercase,\nboth when the filter is saved and by `POST /v1/processors/filter`. To negate a\ncondition, wrap it in `not`.\n\nThe string comparisons (`equals`, `contains`, `startswith`, `endswith` and their\nnegations) are case-sensitive; wrap the field in `lowercase` to compare without case.\n`matches` is not: every pattern is compiled with the flags `gi`, so `^paid$` also\nmatches `PAID` and `[a-z]` matches capital letters. An inline flag such as `(?i)`\nmakes the pattern invalid. The filter still saves, but in a run every record fails\nwith `FILTER_EVALUATION_FAILED` (\"Invalid regular expression: /(?i)…/gi: Invalid\ngroup\"), and `POST /v1/processors/filter` refuses the call with 422 (live-verified\n2026-10-22).\n\n**Logical Operators**\n- `\"and\"`: All conditions must be true\n- `\"or\"`: At least one condition must be true\n- `\"not\"`: Negates the condition\n\n**Field Access and Type Conversion**\n- `\"extract\"`: Access a field from the item by name\n- `\"settings\"`: Access a custom setting from the flow, flow step, or integration configuration\n- `\"boolean\"`: Convert value to Boolean type\n- `\"epochtime\"`: Convert value to Epoch Time (Unix timestamp)\n- `\"number\"`: Convert value to Number type\n- `\"string\"`: Convert value to String type\n\n**Field Access Details**\n\n**Using `extract` to access record fields:**\n- Retrieves values from the current record being processed\n- Can access nested properties using dot notation (e.g., `\"customer.email\"`)\n- Paths are relative to the record itself: `[\"extract\", \"record.status\"]`\n  matches nothing\n- In a record filter, must be wrapped in a type conversion (`string`, `number`,\n  `boolean` or `epochtime`): a bare `[\"extract\", ...]` operand is refused with 422\n  \"Field operation extract must be called by a Type Conversion Operation but was called\n  by <operator>\", both when an import's filter is saved and by\n  `POST /v1/processors/filter`\n\n**Using `settings` to access configuration values:**\n- Retrieves values from the integration's configuration settings\n- Supports different scopes with prefix notation:\n  - `flow.settingName`: Access flow-level settings\n  - `flowGrouping.settingName`: Access the flow group's settings\n  - `export.settingName`: Access the export's (or lookup's) own settings\n  - `import.settingName`: Access the import's own settings\n  - `integration.settingName`: Access integration-level settings\n  - `connection.settingName`, `iClient.settingName`: Access the step's\n    connection and iClient settings\n- A scope whose resource has no settings can be absent; a rule that reads\n  it matches nothing\n- Must be wrapped in a type conversion like `extract`: a bare\n  `[\"settings\", ...]` operand is refused with 422 \"Field operation settings\n  must be called by a Type Conversion Operation\"\n- Useful for dynamic filtering based on configuration\n\n**Field Transformations**\n- `\"lowercase\"`: Convert string to lowercase\n- `\"uppercase\"`: Convert string to uppercase\n- `\"ceiling\"`: Round number up to the nearest integer\n- `\"floor\"`: Round number down to the nearest integer\n- `\"abs\"`: Get absolute value of a number\n\n**Arithmetic** (number operands)\n- `\"add\"`, `\"subtract\"`, `\"multiply\"`, `\"divide\"`, `\"modulo\"`: for example\n  `[\"subtract\", [\"number\", [\"extract\", \"total\"]], [\"number\", [\"extract\", \"discount\"]]]`.\n  `mod` is not an operator (422 \"Invalid operation: mod.\").\n\n**Dates**\n- `epochtime` reads ISO 8601 strings and epoch-millisecond numbers. An empty\n  string, a string that isn't a date, or epoch milliseconds sent as a string\n  fail it with \"Invalid date. Cannot convert given string or number to date\n  time.\", followed by `; record=` and the record as JSON: in a run that record fails (`FILTER_EVALUATION_FAILED`, source\n  `output_filter` on an export), and `POST /v1/processors/filter` and an\n  export preview refuse the whole call with 422. `and` and `or` evaluate\n  every operand, so a `notempty` test beside it doesn't prevent the failure.\n  A missing or null field doesn't match.\n- `datetime`, `date` and `timestamp` are not type conversions: 422\n  \"Invalid operation: <name>.\"\n- `number` reads a missing or empty field as 0.\n\n**Handlebars operands**\n- An operand can also be a string holding exactly one Handlebars expression, such as\n  `\"{{record.status}}\"` or `\"{{lowercase record.code}}\"`, on either side of the\n  operator. It renders against the record (`record.<field>`; an unprefixed\n  `{{status}}` renders empty) before the comparison.\n- The rendered value is a string: wrap it in `[\"number\", ...]` to compare it with a\n  number, or the rule is refused with 422.\n- `epochtime` does not render Handlebars: `[\"epochtime\", \"{{timestamp}}\"]` is refused\n  with 422 \"invalid datetime format\".\n- A string with text around the expression, or with more than one expression, is\n  compared as a literal.\n- `[\"number\", \"{{timestamp \\\"x\\\"}}\"]` is the current time in epoch milliseconds,\n  so `[\"greaterthanequals\", [\"epochtime\", [\"extract\", \"created\"]], [\"subtract\",\n  [\"number\", \"{{timestamp \\\"x\\\"}}\"], 604800000]]` keeps records created in the\n  last 7 days.\n- An unknown helper fails the rule with 422 \"Failed to evaluate handlebars\n  expression\".\n\nChoose the type conversion that matches the comparison — `number` for numeric\ncomparisons, `epochtime` for dates, `string` for text. For example:\n\n```json\n[\n  \"equals\",\n  [\n    \"number\",  // Convert to number before comparison\n    [\n      \"extract\",\n      \"quantity\"\n    ]\n  ],\n  100\n]\n```\n\nExample with datetime conversion:\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",  // Convert to Unix timestamp before comparison\n    [\n      \"extract\",\n      \"createdDate\"\n    ]\n  ],\n  1609459200000  // January 1, 2021 as Unix timestamp in milliseconds\n]\n```\n\nExample with transformations:\n```json\n[\n  \"and\",\n  [\n    \"matches\",\n    [\n      \"lowercase\",  // Convert to lowercase before matching\n      [\n        \"string\",\n        [\n          \"extract\",\n          \"categories\"\n        ]\n      ]\n    ],\n    \"netsuite\"\n  ],\n  [\n    \"notequals\",\n    [\n      \"string\",\n      [\n        \"extract\",\n        \"recurrence.pattern.type\"\n      ]\n    ],\n    \"\"\n  ]\n]\n```\n\nExample comparing a record field with a flow setting:\n```json\n[\n  \"equals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"trantype\"\n    ]\n  ],\n  [\n    \"string\",\n    [\n      \"settings\",\n      \"flow.trantype\"\n    ]\n  ]\n]\n```\n\n**Examples**\n\nExample 1: Status field is not equal to \"cancelled\"\n```json\n[\n  \"notequals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"status\"\n    ]\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"filename\"\n    ]\n  ],\n  \"HC\"\n]\n```\n\nExample 3: Amount is greater than 100\n```json\n[\n  \"greaterthan\",\n  [\n    \"number\",\n    [\n      \"extract\",\n      \"amount\"\n    ]\n  ],\n  100\n]\n```\n\nExample 4: Order date is after January 1, 2023 (UTC). Compare dates as `epochtime`\nagainst epoch milliseconds: a `string` comparison of ISO timestamps orders values with\ndifferent UTC offsets wrongly.\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",\n    [\n      \"extract\",\n      \"orderDate\"\n    ]\n  ],\n  1672531200000\n]\n```\n","items":{"oneOf":[{"title":"String","type":"string"},{"title":"Number","type":"number"},{"title":"Boolean","type":"boolean"},{"title":"Object","type":"object"},{"title":"Array","type":"array"},{"title":"Null","type":"null"}]}}}},"script":{"type":"object","description":"Configuration for programmable script-based filtering. This object enables complex, custom\nfiltering logic beyond what expression-based filtering can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to determine which items\nshould be processed.\n\n**Implementation approach**\n\nScript-based filtering works by:\n1. Executing the specified function from the referenced script\n2. Passing item data to the function\n3. Using the function's return value (true/false) to determine inclusion\n\n**Common use cases**\n\nScript filtering is ideal for:\n- Complex business logic that can't be expressed as simple rules\n- Dynamic filtering criteria that change based on external factors\n- Content-based filtering that requires deep inspection\n- Advanced pattern matching beyond simple wildcards\n- Multi-stage filtering with intermediate logic\n\nFor AI agents: Only use script-based filtering when expression-based filtering is insufficient.\nScript filtering requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to the Script resource that contains the filtering logic. This must be a valid\nObjectId of a Script resource in this account: any other id fails the save with 422\n(\"Only a script owned by the user or a script shared by the Integration App can be used\nat filter.script._scriptId.\").\n\nThe referenced script must contain the function specified in the `function` field\nand must be written to handle filtering specifically. The script receives\nitem data as its input and must return a boolean value indicating whether\nto process the item (true) or skip it (false).\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n"},"function":{"type":"string","description":"Name of the function within the script to execute for filtering decisions. This function\nmust exist in the script referenced by _scriptId.\n\n**Function requirements**\n\nThe specified function must:\n- Accept item data as its first parameter\n- Return a boolean value (true to process the item, false to skip it)\n- Handle errors gracefully\n- Execute efficiently (as it may run for many items)\n\n**Function signature**\n\n```javascript\nfunction filterItems(itemData) {\n  // itemData contains properties of the item being evaluated\n  // Custom logic here\n  return true; // or false to skip the item\n}\n```\n\nFor AI agents: Ensure the function name exactly matches a function defined in the\nreferenced script, as mismatches will cause the filter to fail.\n"}}}}},"Transform":{"type":"object","description":"Configuration for transforming data during processing operations. This object enables\nreshaping of records.\n\n**Transformation capabilities**\n\nCeligo's transformation engine offers powerful features for data manipulation:\n- Precise field mapping with JSONPath expressions\n- Support for any level of nested arrays\n- Formula-based field value generation\n- Dynamic references to flow and integration settings\n\n**Implementation approaches**\n\nThere are two distinct transformation mechanisms available:\n\n**Rule-Based Transformation (`type: \"expression\"`)**\n- **Best For**: Most transformation scenarios from simple to complex\n- **Capabilities**: Field mapping, formula calculations, lookups, nested data handling\n- **Advantages**: Visual configuration, no coding required, intuitive interface\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear mapping requirements or need to reshape data structure\n\n**Script-Based Transformation (`type: \"script\"`)**\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Capabilities**: Full programmatic control, custom processing, complex business rules\n- **Advantages**: Maximum flexibility, can implement any transformation logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Visual transformation tools aren't sufficient for your use case\n","properties":{"type":{"type":"string","description":"Determines which transformation mechanism to use. This choice affects which properties\nmust be configured and how transformation logic is implemented.\n\n**Available types**\n\n**Rule-Based Transformation (`\"expression\"`)**\n- **Required Config**: The `expression` object with mapping definitions\n- **Behavior**: Applies declarative rules to reshape data\n- **Best For**: Most transformation scenarios from simple to complex\n- **Advantages**: Visual configuration, no coding required\n\n**Script-Based Transformation (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to transform data\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard data transformations, use `\"expression\"`\n2. For complex logic or specialized processing, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based transformations. This object enables reshaping data\nwithout requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define transformation rules that\ncan map, modify, and generate data elements.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Field mappings define how input data is transformed to target fields\n- Formulas can be used to calculate or generate new values\n- Lookups can enrich data by fetching related information\n- Mode determines how records are processed (create new or modify existing)\n","properties":{"version":{"type":"string","description":"Version of the expression format. Determines which rules\nproperty contains the transformation logic.\n","enum":["1","2"]},"rules":{"type":"array","description":"Transformation rules for version 1 expressions. An array of\nrule groups; each group is an array of field-mapping objects.\nMost transforms have a single group. Present when `version`\nis `\"1\"`. The output record contains ONLY the generated\nfields — every unmapped field is dropped (v1 has no\nequivalent of Transform 2.0's `modify` mode), and the\nrecord's trace key does not survive the rebuild.\n","items":{"type":"array","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path to read from. Supports multiple\nsyntaxes: bare field names (`id`), dot notation\n(`fulfillment.shipment_id`), slash-prefixed paths\nfor XML (`/FeedProcessingStatus`), wildcards (`*.id`,\n`*.[Internal ID]`), and array indexing (`SDF[0]`).\n"},"generate":{"type":"string","description":"Target field name to write to. Typically a bare name\n(`id`) or dot path (`SDF.Filter.ID`).\n"},"key":{"type":"string","description":"Auto-generated identifier for this rule, used by the\nUI to track individual rules for editing and reordering.\n"}},"required":["extract","generate"]}}},"rulesTwoDotZero":{"type":"object","description":"Configuration for version 2 transformation rules. This object contains the core logic\nfor how data is mapped, enriched, and transformed.\n\n**Capabilities**\n\nTransformation 2.0 provides:\n- Precise field mapping with JSONPath expressions\n- Support for deeply nested data structures\n- Formula-based field generation\n- Dynamic lookups for data enrichment\n- Multiple operating modes to fit different scenarios\n","properties":{"mode":{"type":"string","description":"Transformation mode that determines how records are handled during processing.\n\n**Available modes**\n\n**Create Mode (`\"create\"`)**\n- **Behavior**: Builds entirely new output records from inputs\n- **Use When**: Output structure differs significantly from input\n- **Advantage**: Clean slate approach, no field inheritance\n\n**Modify Mode (`\"modify\"`)**\n- **Behavior**: Makes targeted edits to existing records\n- **Use When**: Output structure should remain similar to input\n- **Advantage**: Preserves unmapped fields from the original record\n","enum":["create","modify"]},"mappings":{"$ref":"#/components/schemas/Mappings"},"lookups":{"allOf":[{"description":"Shared lookup tables used across all mappings defined in the transformation rules.\n\n**Purpose**\n\nLookups provide centralized value translation that can be referenced from any mapping\nin your transformation configuration. They enable consistent translation of codes, IDs,\nand values between systems without duplicating translation logic.\n\n**Usage in transformations**\n\nLookups are particularly valuable in transformations for:\n\n- **Data Normalization**: Standardizing values from diverse source systems\n- **Code Translation**: Converting between different coding systems (e.g., status codes)\n- **Field Enrichment**: Adding descriptive values based on ID or code lookups\n- **Cross-Reference Resolution**: Mapping identifiers between integrated systems\n\n**Implementation**\n\nLookups are defined once in this array and referenced by name in mappings:\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"statusMapping\",\n    \"map\": {\n      \"A\": \"Active\",\n      \"I\": \"Inactive\",\n      \"P\": \"Pending\"\n    },\n    \"default\": \"Unknown Status\"\n  }\n]\n```\n\nThen referenced in mappings using the lookupName property:\n\n```json\n{\n  \"generate\": \"status\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.statusCode\",\n  \"lookupName\": \"statusMapping\"\n}\n```\n\nThe system automatically applies the lookup during transformation processing.\n\nFor complete details on lookup properties and behavior, see the Lookups schema.\n"},{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the JSON shape the transformTwoDotZero processor\nevaluates `mappings[].extract` JSONPath values against at\nflow runtime. Applies only to Transform 2.0 (v2,\n`rulesTwoDotZero`); v1 transforms (the `rules` array on\n`transform.expression.rules`) and script-mode transforms\nignore this field.\n"}}}}},"script":{"type":"object","description":"Configuration for programmable script-based transformations. This object enables complex, custom\ntransformation logic beyond what expression-based transformations can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to transform data according to\nspecialized business rules or complex algorithms.\n\n**Implementation approach**\n\nScript-based transformation works by:\n1. Executing the specified function from the referenced script\n2. Passing input data to the function\n3. Using the function's return value as the transformed output\n\n**Common use cases**\n\nScript transformation is ideal for:\n- Complex business logic that can't be expressed through mappings\n- Algorithmic transformations requiring computation\n- Dynamic transformations based on external factors\n- Legacy system data format compatibility\n- Multi-stage processing with intermediate steps\n\nOnly use script-based transformation when expression-based transformation is insufficient.\nScript transformation requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to a predefined script resource containing the transformation logic.\n\nThe referenced script should contain the function specified in the\n'function' property. It must be a script in this account: any other id fails the save\nwith 422 (\"Only a script owned by the user or a script shared by the Integration App can\nbe used at transform.script._scriptId.\").\n","format":"objectid"},"function":{"type":"string","description":"Name of the function within the script to execute for transformation. This function\nmust exist in the script referenced by _scriptId.\n"}}}}},"Lookups":{"type":"array","description":"Configuration for value-to-value transformations using lookup tables.\n\n**Purpose**\n\nLookups provide a way to translate values from one system to another. They transform\ninput values into output values using either static mapping tables or\ndynamic lookup caches.\n\n**Lookup mechanisms**\n\nThere are two distinct lookup mechanisms available:\n\n1. **Static Lookups**: Define a simple key-value map object and store it as part of your resource\n   - Best for: Small, fixed sets of values that rarely change\n   - Implementation: Configure the `map` object with input-to-output value mappings\n   - Example: Country codes, status values, simple translations\n\n2. **Dynamic Lookups**: Reference an existing 'Lookup Cache' resource in your Celigo account\n   - Best for: Large datasets, frequently changing values, or complex reference data\n   - Implementation: Configure `_lookupCacheId` to reference cached data maintained independently\n   - Example: Product catalogs, customer databases, pricing information\n\n**Property usage**\n\nThere are two mutually exclusive ways to configure lookups, depending on which mechanism you choose:\n\n1. **For Static Mappings**: Configure the `map` property with a direct key-value object\n   ```json\n   \"map\": {\"US\": \"United States\", \"CA\": \"Canada\"}\n   ```\n\n2. **For Dynamic Lookups**: Configure the following properties:\n   - `_lookupCacheId`: Reference to the lookup cache resource\n   - `extract`: JSON path to extract specific value from the returned lookup object\n\n**When to use**\n\nLookups are ideal for:\n\n1. **Value Translation**: Mapping codes or IDs to human-readable values\n\n2. **Data Enrichment**: Adding related information to records during processing\n\n3. **Normalization**: Ensuring consistent formatting of values across systems\n\n**Implementation details**\n\nLookups can be referenced in:\n\n1. **Field Mappings**: Direct use in field transformation configurations\n\n2. **Handlebars Templates**: Use within templates with the syntax:\n   ```\n   {{lookup 'lookupName' record.fieldName}}\n   ```\n\n**Example usage**\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"countryCodeToName\",\n    \"map\": {\n      \"US\": \"United States\",\n      \"CA\": \"Canada\",\n      \"UK\": \"United Kingdom\"\n    },\n    \"default\": \"Unknown Country\",\n    \"allowFailures\": true\n  },\n  {\n    \"name\": \"productDetails\",\n    \"_lookupCacheId\": \"60a2c4e6f321d800129a1a3c\",\n    \"extract\": \"$.details.price\",\n    \"allowFailures\": false\n  }\n]\n```\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for the lookup table within this configuration.\n\nThis name must be unique within the scope where the lookup is defined and is used to reference\nthe lookup in handlebars templates with the syntax {{lookup 'name' value}}.\n\nChoose descriptive names that indicate the transformation purpose, such as:\n- \"countryCodeToName\" for country code to full name conversion\n- \"statusMapping\" for status code translations\n- \"departmentCodes\" for department code to name mapping\n"},"map":{"type":["object","null"],"description":"The lookup mapping table as key-value pairs. The platform stores `null`\nhere on dynamic lookups, which resolve values at runtime instead of\nfrom a static table.\n\nThis object contains the input values as keys and their corresponding\noutput values. When a input value matches a key in this object,\nit will be replaced with the corresponding value.\n\nThe map should be kept to a reasonable size (typically under 100 entries)\nfor optimal performance. For larger mapping requirements, consider using\ndynamic lookups instead.\n\nMaps can include:\n- Simple code to name conversions: {\"US\": \"United States\"}\n- Status transformations: {\"A\": \"Active\", \"I\": \"Inactive\"}\n- ID to name mappings: {\"100\": \"Marketing\", \"200\": \"Sales\"}\n\nValues can be strings, numbers, or booleans, but all are stored as strings\nin the configuration.\n"},"_lookupCacheId":{"type":"string","description":"Reference to a LookupCache resource that contains the reference data for the lookup.\n\n**Purpose**\n\nThis field connects the lookup to an external data source that has been cached in the system.\nUnlike static lookups that use the `map` property, dynamic lookups can reference large datasets\nor frequently changing information without requiring constant updates to the integration.\n\n**Implementation details**\n\nThe LookupCache resource referenced by this ID contains:\n- The data records to be used as a reference source\n- Configuration for how the data should be indexed and accessed\n- Caching parameters to balance performance with data freshness\n\n**Usage patterns**\n\nCommonly used to reference:\n- Product catalogs or SKU databases\n- Customer or account information\n- Pricing tables or discount rules\n- Complex business logic lookup tables\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n","format":"objectid"},"extract":{"type":"string","description":"JSON path expression that extracts a specific value from the cached lookup object.\n\n**Purpose**\n\nWhen using dynamic lookups with a LookupCache, this JSON path identifies which field to extract\nfrom the cached object after it has been retrieved using the lookup key.\n\n**Implementation details**\n\n- Must use JSON path syntax (similar to mapping extract fields)\n- Operates on the cached object returned by the lookup operation\n- Examples:\n  - \"$.name\" - Extract the name field from the top level\n  - \"$.details.price\" - Extract a nested price field\n  - \"$.attributes[0].value\" - Extract a value from the first element of an array\n\n**Usage scenario**\n\nWhen a lookup cache contains complex objects:\n```json\n// Cache entry for key \"PROD-123\":\n{\n  \"id\": \"PROD-123\",\n  \"name\": \"Premium Widget\",\n  \"details\": {\n    \"price\": 99.99,\n    \"currency\": \"USD\",\n    \"inStock\": true\n  }\n}\n```\n\nSetting extract to \"$.details.price\" would return 99.99 as the lookup result.\n\nIf no extract is provided, the entire cached object is returned as the lookup result.\n"},"default":{"type":["string","null"],"description":"Default value to use when the source value is not found in the lookup map.\nThe platform stores `null` here when no default is configured.\n\nThis value is used as a fallback when:\n1. The source value doesn't match any key in the map\n2. allowFailures is true or omitted\n\nSetting an appropriate default helps prevent flow failures due to unexpected\nvalues and provides predictable behavior for edge cases.\n\nCommon default patterns include:\n- Descriptive unknowns: \"Unknown Country\", \"Unspecified Status\"\n- Original value indicators: \"{Original Value}\", \"No mapping found\"\n- Neutral values: \"Other\", \"N/A\", \"Miscellaneous\"\n\nA save refuses a default together with an explicit `allowFailures: false`\n(422 `Lookups with default set can not have allowFailures explicitly set to\nfalse.`): a lookup either fails a miss (`allowFailures: false`, no default) or\nfalls back to its default.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, missing lookup values will use the default value rather than causing an error.\n\n**Behavior control**\n\nThis field determines how the system handles source values that don't exist in the map:\n\n- true: Use the default value for missing mappings and continue processing\n- false: Treat missing mappings as errors, failing the record (`lookup_error` for a\n  static map, `lookupcache_evaluation_failed` for a lookup cache)\n- omitted or null: the record doesn't fail either. On an import a static lookup writes\n  its default if it has one, and a static or cache lookup without one leaves the\n  mapped field out of the output, with no error\n\n**Recommendation**\n\nSet this to true when:\n- New source values might appear over time\n- Data quality issues could introduce unexpected values\n- Processing should continue even with imperfect mapping\n\nSet this to false when:\n- Complete data accuracy is critical\n- All possible source values are known and controlled\n- Missing mappings indicate serious data problems that should be addressed\n\nA default raises no error: records that miss go out with the default value and\nnothing reports them. Use true only when that fallback is acceptable at the\ndestination, and set false explicitly when a missing value must stop the record.\n"}}}},"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: one in the script named by `_scriptId`, or one the code deployed on\nthe stack named by `_stackId` exposes."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`. Send this or `_stackId`, not both.\nMust be a script in this account: any other id fails the save with 422, naming the hook,\nsuch as \"Only a script owned by the user or a script shared by the Integration App can\nbe used at hooks.preMap._scriptId.\" on an import's `preMap`."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments.\nThe API doesn't enforce the either/or: a save carrying both `_scriptId` and `_stackId`\nanswers 200 and stores both, and the script and the stack then each list the step as a\ndependent."},"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."}}},"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"},"AiAgent":{"type":"object","required":["_id","name","adaptorType","aiAgent","createdAt","lastModified"],"description":"AI agent import object as returned by the API.","allOf":[{"$ref":"#/components/schemas/AiAgentBase"},{"$ref":"#/components/schemas/ResourceResponse"},{"type":"object","properties":{"_sourceId":{"type":"string","format":"objectId","readOnly":true,"description":"Export that feeds data into this import within a flow."},"aiDescription":{"$ref":"#/components/schemas/AIDescription"},"apiIdentifier":{"type":"string","readOnly":true,"description":"Ten-character hex identifier for the resource."},"lookups":{"type":"array","readOnly":true,"description":"Lookup configurations associated with this import.","items":{"type":"object"}},"_templateId":{"type":"string","format":"objectId","readOnly":true,"description":"Template this AI agent was created from."},"draftExpiresAt":{"type":"string","format":"date-time","readOnly":true,"description":"When the draft version of this AI agent expires. Drafts are staged by Celigo's\nAI assistant when it proposes changes for review; this field is present only\nwhile an unapproved draft is pending."},"debugUntil":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp until which verbose debug logging runs for this AI agent. Read-only\non create/update; enable it through the debug PATCH endpoint, not a full update."}}}]},"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"]},"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"}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"400-bad-request":{"description":"Bad request. The server could not understand the request because of malformed syntax or invalid parameters.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/imports/{_id}":{"put":{"summary":"Update an AI agent","description":"Replaces the full configuration of an AI agent import.","operationId":"updateAiAgent","tags":["AI Agents"],"parameters":[{"name":"_id","in":"path","description":"Import resource ID.","required":true,"schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Request"}}}},"responses":{"200":{"description":"Updated AI agent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiAgent"}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
````

## Delete an AI agent

> Deletes an AI agent import. Soft-deleted and retained in the recycle bin\
> for 30 days before permanent removal.

```json
{"openapi":"3.2.0","info":{"title":"AI Agents","version":"1.0.0"},"tags":[{"name":"AI Agents","description":"AI agents are LLM-powered import steps that classify, extract, validate, or\ngenerate data within integration flows. They are stored as import resources\nwith `adaptorType: AiAgentImport` and managed via the `/v1/imports` endpoints.\n\nThree built-in providers — OpenAI (via the Responses API), Google Gemini (via\nLiteLLM), and Anthropic Claude (via LiteLLM) — run on platform-managed\ncredentials or, with a `_connectionId`, on your own key (BYOK). Catalog\nproviders — Mistral, xAI, Hugging Face, DeepSeek, Cohere, and Groq — use one generic\nflat configuration format and always run on a BYOK connection.\n\n## AI agent schema\n\n{% openapi-schemas spec=\"ai-agent\" schemas=\"AiAgent\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-dependency-conflict":{"description":"The resource has dependents that must be deleted first. Each entry\nin the `errors` array names one blocking resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}}},"paths":{"/v1/imports/{_id}":{"delete":{"summary":"Delete an AI agent","description":"Deletes an AI agent import. Soft-deleted and retained in the recycle bin\nfor 30 days before permanent removal.","operationId":"deleteAiAgent","tags":["AI Agents"],"parameters":[{"name":"_id","in":"path","description":"Import resource ID.","required":true,"schema":{"type":"string","format":"objectId"}}],"responses":{"204":{"description":"AI agent deleted."},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"},"422":{"$ref":"#/components/responses/422-dependency-conflict"}}}}}}
```

## Preview and compare AI agent configurations

> Runs one or more AI agent import configurations against the same input\
> data and returns the LLM response, token usage, and timing for each.\
> No job is created and no state is updated — this is a stateless preview.\
> \
> The UI uses this to compare model or prompt variations side-by-side\
> before saving. Each entry in \`imports\` is a full import object (the same\
> shape returned by \`GET /v1/imports/{\_id}\`) with the \`aiAgent\` block\
> configured for the variant being tested.\
> \
> Each entry in \`imports\` must include \`adaptorType: "AiAgentImport"\` and\
> a valid \`aiAgent\` configuration. A \`\_connectionId\` is required unless the\
> agent is on a built-in provider (\`openai\`, \`gemini\`, \`anthropic\`) using\
> platform-managed credentials — catalog providers always need one, since\
> the preview runs the model. The response array is positional\
> — \`response\[i]\` corresponds to \`imports\[i]\`. A per-entry\
> \`statusCode: 422\` with \`errors\` means that specific configuration failed,\
> not that the overall request failed.

````json
{"openapi":"3.2.0","info":{"title":"AI Agents","version":"1.0.0"},"tags":[{"name":"AI Agents","description":"AI agents are LLM-powered import steps that classify, extract, validate, or\ngenerate data within integration flows. They are stored as import resources\nwith `adaptorType: AiAgentImport` and managed via the `/v1/imports` endpoints.\n\nThree built-in providers — OpenAI (via the Responses API), Google Gemini (via\nLiteLLM), and Anthropic Claude (via LiteLLM) — run on platform-managed\ncredentials or, with a `_connectionId`, on your own key (BYOK). Catalog\nproviders — Mistral, xAI, Hugging Face, DeepSeek, Cohere, and Groq — use one generic\nflat configuration format and always run on a BYOK connection.\n\n## AI agent schema\n\n{% openapi-schemas spec=\"ai-agent\" schemas=\"AiAgent\" 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":{"PreviewCompareRequest":{"type":"object","description":"Request body for comparing AI agent configurations side-by-side. Each\nentry in `imports` is a full import object with\n`adaptorType: \"AiAgentImport\"`. The same `_id` can appear in multiple\nentries with different `aiAgent` settings to compare model or prompt\nvariants.","required":["imports"],"properties":{"imports":{"type":"array","description":"AI agent import configurations to run. Each entry is executed\nindependently against the same `data` input. The response array\nis positional — result `[i]` corresponds to `imports[i]`.","items":{"$ref":"#/components/schemas/Request"}},"data":{"type":"object","additionalProperties":true,"description":"Input record passed to each agent. Use `{}` when the agent's prompt\ngenerates content without needing input data."}}},"Request":{"type":"object","description":"Configuration for an AI agent import. Set `aiAgent.provider` to choose\nthe LLM backend, then configure the matching format: `aiAgent.openai` for\nOpenAI, `aiAgent.litellm` for Gemini and Anthropic Claude, or the generic\nflat format (`aiAgent.model`, `aiAgent.modelOptions`, …) for the catalog\nproviders. Omit `_connectionId` to use platform-managed credentials on a\nbuilt-in provider; catalog providers need a BYOK connection to run.","required":["name","adaptorType","aiAgent"],"allOf":[{"$ref":"#/components/schemas/AiAgentBase"}]},"AiAgentBase":{"type":"object","description":"Writable AI agent import fields shared by the request and response schemas. Set\n`aiAgent.provider` to choose the LLM backend, then configure the matching format:\n`aiAgent.openai` for OpenAI, `aiAgent.litellm` for Gemini and Anthropic Claude\n(with provider-specific settings under `aiAgent.litellm._overrides`), or the\ngeneric flat format (`aiAgent.model`, `aiAgent.modelOptions`, …) for the catalog\nproviders Mistral, xAI, Hugging Face, DeepSeek, Cohere, and Groq.","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Display name for the AI agent."},"description":{"type":"string","description":"Optional description of the AI agent's purpose."},"_connectionId":{"type":"string","format":"objectId","description":"Connection for BYOK (bring your own key) — an HTTP connection to the vendor's API.\nOptional for the built-in providers (`openai`, `gemini`, `anthropic`), which run on\nplatform-managed credentials when it is omitted. Catalog providers (`mistral`, `xai`,\n`huggingface`, `deepseek`, `cohere`, `groq`) have no platform-managed credentials: the save\nrequires it — without one the agent fails to save with 422 `connection_required` on\n`_connectionId`."},"_integrationId":{"type":"string","format":"objectId","description":"Integration this AI agent belongs to. Omitted for a standalone agent."},"adaptorType":{"type":"string","enum":["AiAgentImport"],"description":"Must be `AiAgentImport`."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this AI agent was created from, set when it is part of an installed integration app."},"aiAgent":{"$ref":"#/components/schemas/AiAgentConfig"},"mappings":{"allOf":[{"$ref":"#/components/schemas/Mappings"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Selects the data context that `mappings[].extract` evaluates against. With no\nmappings the agent receives the entire record as JSON (or only the record's own\ntop-level `text`, `record` or `files` fields, when it has any) and `inputContext`\nis moot. `record`\n(the default) extracts against the flat record; `envelope` extracts against the\nruntime envelope, which exposes the record alongside job, settings, and other\nrun-time metadata (`$.record.field`, `$.settings.field`, etc.). Same contract as\nthe import resource's `inputContext`."},"filter":{"description":"Filter applied to incoming records before they are sent to the LLM. Records that match\ncontinue through the agent; records that don't skip it, counted as ignored with no model\ncall, and continue to the next step.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"responseTransform":{"description":"Transformation that reshapes the LLM's response after records are processed, before the\nresponse is handled by response mappings and downstream steps.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the agent's import lifecycle.\nAI agents do not aggregate records, so the `postAggregate` hook that other import\ntypes expose is intentionally omitted here.","properties":{"preMap":{"description":"Hook that runs on each page of records before the agent's mappings are applied.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postMap":{"description":"Hook that runs after mappings are applied, before records are sent to the LLM.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postSubmit":{"description":"Hook that runs after the LLM responds, with both the submitted records and the responses.","allOf":[{"$ref":"#/components/schemas/Hook"}]}}},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"distributed":{"type":"boolean","description":"Always false for AI agent imports — the agent executes on Celigo's servers, not inside a target application."},"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 are silently skipped. Rarely meaningful for AI agents; accepted for import-surface parity."},"ignoreMissing":{"type":"boolean","description":"When true, records that do not already exist in the destination are silently skipped. Rarely meaningful for AI agents; accepted for import-surface parity."},"mockResponse":{"type":"array","description":"Predefined response records used in place of calling the LLM when testing the agent,\nso flows can run without spending tokens. Records must be in integrator.io canonical\nformat — 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 for structured-output agents."},"_text":{"type":"string","description":"Mock response body for text-output agents."},"_headers":{"type":"object","description":"Mock response headers."},"blobKey":{"type":"string","description":"Mock output of an agent with `blob` output (image generation:\n`openai.output.format.type` or `litellm.responseFormat.type` set to `blob`),\nwhich returns the stored image's key here. Unrelated to the import-level `blob`\nflag, which agents refuse."}}}}}},"AiAgentConfig":{"type":"object","description":"AI agent configuration for an AiAgentImport. An `ai_agent` guardrail is configured by\nthe guardrail spec's `GuardrailAiAgentConfig` instead, which has no `tools` arrays.\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 the\nsave requires one — without it the agent fails to save with 422\n`connection_required` on `_connectionId`.\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","max"],"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":"Caps the length of the model's response, in tokens. An agent created without it is\nsaved with 5000; raise it for long outputs such as large extractions."},"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":"Which editor the UI opens `jsonSchema` in; the schema sent to the provider\nis the same either way, and the field is only meaningful when `type` is\n\"json_schema\". Unset opens the visual editor, and an edit there rebuilds\n`jsonSchema` from the field list, keeping only `type`, `properties`,\n`required`, `items`, `description`, `enum`, `pattern`, length and numeric\nbounds, `multipleOf`, `minItems`/`maxItems` and a `\"null\"` type — other\nkeywords are dropped without a prompt, and `integer` becomes `number`."},"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`."}}},"schema":{"type":"object","readOnly":true,"additionalProperties":true,"description":"Server-written copy of `jsonSchema`, returned whenever `jsonSchema` is set — on\nAI agent imports and on guardrail agents alike. Send `jsonSchema`, not this field."}},"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","code_execution","tool"],"description":"Type of tool. Every type except `code_execution` is configured through the\nsibling object of the same name; OpenAI spells this one `code_execution`, while\nthe Gemini and Anthropic overrides spell theirs `codeExecution` — the other spelling\nfails with 422 `enum`."},"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"]}}}},"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":"Which editor the UI opens `jsonSchema` in; the schema sent to the provider is\nthe same either way, and the field is only meaningful when `type` is\n\"json_schema\". Unset opens the visual editor, and an edit there rebuilds\n`jsonSchema` from the field list, keeping only `type`, `properties`, `required`,\n`items`, `description`, `enum`, `pattern`, length and numeric bounds,\n`multipleOf`, `minItems`/`maxItems` and a `\"null\"` type — other keywords are\ndropped without a prompt, and `integer` becomes `number`."},"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","codeExecution","mcp","tool"],"description":"Type of Gemini tool. `codeExecution` is the Gemini spelling — `code_execution`\nfails with 422 `enum`."},"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"]}}}},"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","codeExecution"],"description":"Type of Claude tool. `codeExecution` is the Anthropic spelling — `code_execution`\nfails with 422 `enum`."},"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). Each\nentry is either a plain tool name or an object carrying display\nmetadata — same contract as the OpenAI and Gemini `allowedTools`.","items":{"oneOf":[{"title":"name","type":"string","description":"Tool name as exposed by the MCP server."},{"title":"tool","type":"object","required":["name"],"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."}}}]}}}},"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."}}}}}}}}},"Mappings":{"type":"array","description":"Array of field mapping configurations for transforming data from one format into another.\n\n**Guidance**\n\nThis schema is designed around RECURSION as its core architectural principle. Understanding this recursive\nnature is essential for building effective mappings:\n\n1. The schema is self-referential by design - a mapping can contain nested mappings of the same structure\n2. Complex data structures (nested objects and arrays of objects, to any depth) are ALL\n   handled through this recursive pattern; an array of arrays is not (build one in a script hook)\n3. Each mapping handles one level of the data structure; deeper levels are handled by nested mappings\n\nWhen generating mappings programmatically:\n- For simple fields (string, number, boolean): Create single mapping objects\n- For objects: Create a parent mapping with nested 'mappings' array containing child field mappings\n- For arrays: Use 'buildArrayHelper' with extract paths defining array inputs and\n  recursive 'mappings' to define object structures\n\nThe system will process these nested structures recursively during runtime, ensuring proper construction\nof complex hierarchical data while maintaining excellent performance.\n\nOn save, a leaf row with neither a source (`extract`, `hardCodedValue`, `lookupName`) nor a\ndestination (`generate`) is dropped silently, at every nesting level, and so is an object or\nobject-array row left with no source, no destination and no remaining child `mappings` or\n`buildArrayHelper` entries; a row with only one of the two, and a source-less, destination-less\nwrapper that still has children, are kept.\n\nThe shape of the array is the mapper's **JSON structure**. Named top-level items produce an object\nrecord (`record { }`). Exactly one top-level item with `dataType: objectarray` and no `generate` is\nthe `rows [ ]` structure: the mapped record is an array — usually one element per element of the\nsource array that root's `buildArrayHelper` iterates — Handlebars templates such as `http.body`\nbind it as `rows` instead of `record`, and the mapping editor shows that row's destination disabled.\n","items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}}},"items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}},"Filter":{"type":"object","description":"Configuration for selectively processing records based on specified criteria. This object enables\nprecise control over which items are included or excluded from processing operations.\n\n**Filter behavior**\n\nWhen configured, the filter is applied before processing begins:\n- Items that match the filter criteria are processed\n- Items that don't match are completely skipped\n- No partial processing is performed\n\nSend the whole object. A rules array sent alone as the `filter` value is not\nstored, and a full update of an export that carries one removes the filter the\nexport had, with no error.\n\n**Implementation approaches**\n\nThere are two distinct filtering mechanisms available:\n\n**Rule-Based Filtering (`type: \"expression\"`)**\n- **Best For**: Common filtering patterns based on standard attributes\n- **Capabilities**: Filter by names, values, dates, numerical ranges, text patterns\n- **Advantages**: Declarative, no coding required, consistent performance\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear, static criteria for selection\n\n**Script-Based Filtering (`type: \"script\"`)**\n- **Best For**: Complex logic, dynamic criteria, or business rules\n- **Capabilities**: Full programmatic control, access to complete metadata\n- **Advantages**: Maximum flexibility, can implement any filtering logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Simple rules aren't sufficient or logic needs to be dynamic\n","properties":{"type":{"type":"string","description":"Determines which filtering mechanism to use. This choice affects which properties\nmust be configured and how filtering logic is implemented.\n\n**Available types**\n\n**Rule-Based Filtering (`\"expression\"`)**\n- **Required Config**: The `expression` object with rule definitions\n- **Behavior**: Evaluates declarative rules against item attributes\n- **Best For**: Common patterns like name matching, date ranges, value limits\n- **Advantages**: Simpler to configure, no custom code required\n\n**Script-Based Filtering (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to determine which items to process\n- **Best For**: Complex conditions, business logic, dynamic criteria\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard filtering needs (name, size, date), use `\"expression\"`\n2. For complex logic or conditions not covered by expressions, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based filtering. This object enables filtering\nitems based on common attributes without requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define filtering rules that\ncan match against item attributes like name, type, value, date, and other properties.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Rules can be combined with AND/OR logic\n- Each rule can check a specific attribute\n- Multiple conditions can be applied (ranges, pattern matching, exact matches)\n\n**Common filter patterns**\n\n1. **Pattern matching**: Regular expressions with `matches`\n2. **Value range filtering**: Numbers between min and max values\n3. **Date range filtering**: Items created/modified within specific time ranges\n4. **Status checking**: Items with specific status values or properties\n\nFor AI agents: Rule-based filtering should be your first choice when the filtering criteria\ncan be expressed in terms of standard attributes. Only use script-based filtering when\nmore complex logic is required.\n","properties":{"version":{"type":"string","description":"Version identifier for the expression format. Currently only version \"1\" is supported.\n\nThis field ensures future compatibility if the expression format evolves. Always set to \"1\"\nfor current implementations.\n","enum":["1"]},"rules":{"type":"array","description":"Expression array defining filter conditions using prefix notation. The first element is the operator,\nfollowed by its operands which may themselves be nested expression arrays.\n\nThe rule expression follows this pattern:\n- First element: Operator name (string)\n- Remaining elements: Operands for that operator (values or nested expressions)\n\n**Expression structure**\n\nFilter expressions use a prefix notation where operators appear before their operands:\n```\n[operator, operand1, operand2, ...]\n```\n\n**Comparison Operators**\n- `\"equals\"`: Exact match (equals)\n- `\"notequals\"`: Not equal to value (not equals)\n- `\"greaterthan\"`: Value is greater than specified value (is greater than)\n- `\"greaterthanequals\"`: Value is greater than or equal to specified value (is greater than or equals)\n- `\"lessthan\"`: Value is less than specified value (is less than)\n- `\"lessthanequals\"`: Value is less than or equal to specified value (is less than or equals)\n- `\"startswith\"`: String starts with specified prefix (starts with)\n- `\"notstartswith\"`: String does not start with specified prefix (does not start with)\n- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"notendswith\"`: String does not end with specified suffix (does not end with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"empty\"`: Field is missing or empty (is empty)\n- `\"notempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches a regular expression, ignoring case (matches)\n\nOperator names are case-insensitive: `NOTEMPTY` and `endsWith` work and are stored\nas sent. `isempty`, `isnotempty`, `anyof`, `notcontains`, `doesnotstartwith`,\n`doesnotendwith`, `notmatches`, `doesnotmatch` and `notequal` are not operators:\nthey are refused with 422 \"Invalid operation: <name>\", with the name in lowercase,\nboth when the filter is saved and by `POST /v1/processors/filter`. To negate a\ncondition, wrap it in `not`.\n\nThe string comparisons (`equals`, `contains`, `startswith`, `endswith` and their\nnegations) are case-sensitive; wrap the field in `lowercase` to compare without case.\n`matches` is not: every pattern is compiled with the flags `gi`, so `^paid$` also\nmatches `PAID` and `[a-z]` matches capital letters. An inline flag such as `(?i)`\nmakes the pattern invalid. The filter still saves, but in a run every record fails\nwith `FILTER_EVALUATION_FAILED` (\"Invalid regular expression: /(?i)…/gi: Invalid\ngroup\"), and `POST /v1/processors/filter` refuses the call with 422 (live-verified\n2026-10-22).\n\n**Logical Operators**\n- `\"and\"`: All conditions must be true\n- `\"or\"`: At least one condition must be true\n- `\"not\"`: Negates the condition\n\n**Field Access and Type Conversion**\n- `\"extract\"`: Access a field from the item by name\n- `\"settings\"`: Access a custom setting from the flow, flow step, or integration configuration\n- `\"boolean\"`: Convert value to Boolean type\n- `\"epochtime\"`: Convert value to Epoch Time (Unix timestamp)\n- `\"number\"`: Convert value to Number type\n- `\"string\"`: Convert value to String type\n\n**Field Access Details**\n\n**Using `extract` to access record fields:**\n- Retrieves values from the current record being processed\n- Can access nested properties using dot notation (e.g., `\"customer.email\"`)\n- Paths are relative to the record itself: `[\"extract\", \"record.status\"]`\n  matches nothing\n- In a record filter, must be wrapped in a type conversion (`string`, `number`,\n  `boolean` or `epochtime`): a bare `[\"extract\", ...]` operand is refused with 422\n  \"Field operation extract must be called by a Type Conversion Operation but was called\n  by <operator>\", both when an import's filter is saved and by\n  `POST /v1/processors/filter`\n\n**Using `settings` to access configuration values:**\n- Retrieves values from the integration's configuration settings\n- Supports different scopes with prefix notation:\n  - `flow.settingName`: Access flow-level settings\n  - `flowGrouping.settingName`: Access the flow group's settings\n  - `export.settingName`: Access the export's (or lookup's) own settings\n  - `import.settingName`: Access the import's own settings\n  - `integration.settingName`: Access integration-level settings\n  - `connection.settingName`, `iClient.settingName`: Access the step's\n    connection and iClient settings\n- A scope whose resource has no settings can be absent; a rule that reads\n  it matches nothing\n- Must be wrapped in a type conversion like `extract`: a bare\n  `[\"settings\", ...]` operand is refused with 422 \"Field operation settings\n  must be called by a Type Conversion Operation\"\n- Useful for dynamic filtering based on configuration\n\n**Field Transformations**\n- `\"lowercase\"`: Convert string to lowercase\n- `\"uppercase\"`: Convert string to uppercase\n- `\"ceiling\"`: Round number up to the nearest integer\n- `\"floor\"`: Round number down to the nearest integer\n- `\"abs\"`: Get absolute value of a number\n\n**Arithmetic** (number operands)\n- `\"add\"`, `\"subtract\"`, `\"multiply\"`, `\"divide\"`, `\"modulo\"`: for example\n  `[\"subtract\", [\"number\", [\"extract\", \"total\"]], [\"number\", [\"extract\", \"discount\"]]]`.\n  `mod` is not an operator (422 \"Invalid operation: mod.\").\n\n**Dates**\n- `epochtime` reads ISO 8601 strings and epoch-millisecond numbers. An empty\n  string, a string that isn't a date, or epoch milliseconds sent as a string\n  fail it with \"Invalid date. Cannot convert given string or number to date\n  time.\", followed by `; record=` and the record as JSON: in a run that record fails (`FILTER_EVALUATION_FAILED`, source\n  `output_filter` on an export), and `POST /v1/processors/filter` and an\n  export preview refuse the whole call with 422. `and` and `or` evaluate\n  every operand, so a `notempty` test beside it doesn't prevent the failure.\n  A missing or null field doesn't match.\n- `datetime`, `date` and `timestamp` are not type conversions: 422\n  \"Invalid operation: <name>.\"\n- `number` reads a missing or empty field as 0.\n\n**Handlebars operands**\n- An operand can also be a string holding exactly one Handlebars expression, such as\n  `\"{{record.status}}\"` or `\"{{lowercase record.code}}\"`, on either side of the\n  operator. It renders against the record (`record.<field>`; an unprefixed\n  `{{status}}` renders empty) before the comparison.\n- The rendered value is a string: wrap it in `[\"number\", ...]` to compare it with a\n  number, or the rule is refused with 422.\n- `epochtime` does not render Handlebars: `[\"epochtime\", \"{{timestamp}}\"]` is refused\n  with 422 \"invalid datetime format\".\n- A string with text around the expression, or with more than one expression, is\n  compared as a literal.\n- `[\"number\", \"{{timestamp \\\"x\\\"}}\"]` is the current time in epoch milliseconds,\n  so `[\"greaterthanequals\", [\"epochtime\", [\"extract\", \"created\"]], [\"subtract\",\n  [\"number\", \"{{timestamp \\\"x\\\"}}\"], 604800000]]` keeps records created in the\n  last 7 days.\n- An unknown helper fails the rule with 422 \"Failed to evaluate handlebars\n  expression\".\n\nChoose the type conversion that matches the comparison — `number` for numeric\ncomparisons, `epochtime` for dates, `string` for text. For example:\n\n```json\n[\n  \"equals\",\n  [\n    \"number\",  // Convert to number before comparison\n    [\n      \"extract\",\n      \"quantity\"\n    ]\n  ],\n  100\n]\n```\n\nExample with datetime conversion:\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",  // Convert to Unix timestamp before comparison\n    [\n      \"extract\",\n      \"createdDate\"\n    ]\n  ],\n  1609459200000  // January 1, 2021 as Unix timestamp in milliseconds\n]\n```\n\nExample with transformations:\n```json\n[\n  \"and\",\n  [\n    \"matches\",\n    [\n      \"lowercase\",  // Convert to lowercase before matching\n      [\n        \"string\",\n        [\n          \"extract\",\n          \"categories\"\n        ]\n      ]\n    ],\n    \"netsuite\"\n  ],\n  [\n    \"notequals\",\n    [\n      \"string\",\n      [\n        \"extract\",\n        \"recurrence.pattern.type\"\n      ]\n    ],\n    \"\"\n  ]\n]\n```\n\nExample comparing a record field with a flow setting:\n```json\n[\n  \"equals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"trantype\"\n    ]\n  ],\n  [\n    \"string\",\n    [\n      \"settings\",\n      \"flow.trantype\"\n    ]\n  ]\n]\n```\n\n**Examples**\n\nExample 1: Status field is not equal to \"cancelled\"\n```json\n[\n  \"notequals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"status\"\n    ]\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"filename\"\n    ]\n  ],\n  \"HC\"\n]\n```\n\nExample 3: Amount is greater than 100\n```json\n[\n  \"greaterthan\",\n  [\n    \"number\",\n    [\n      \"extract\",\n      \"amount\"\n    ]\n  ],\n  100\n]\n```\n\nExample 4: Order date is after January 1, 2023 (UTC). Compare dates as `epochtime`\nagainst epoch milliseconds: a `string` comparison of ISO timestamps orders values with\ndifferent UTC offsets wrongly.\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",\n    [\n      \"extract\",\n      \"orderDate\"\n    ]\n  ],\n  1672531200000\n]\n```\n","items":{"oneOf":[{"title":"String","type":"string"},{"title":"Number","type":"number"},{"title":"Boolean","type":"boolean"},{"title":"Object","type":"object"},{"title":"Array","type":"array"},{"title":"Null","type":"null"}]}}}},"script":{"type":"object","description":"Configuration for programmable script-based filtering. This object enables complex, custom\nfiltering logic beyond what expression-based filtering can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to determine which items\nshould be processed.\n\n**Implementation approach**\n\nScript-based filtering works by:\n1. Executing the specified function from the referenced script\n2. Passing item data to the function\n3. Using the function's return value (true/false) to determine inclusion\n\n**Common use cases**\n\nScript filtering is ideal for:\n- Complex business logic that can't be expressed as simple rules\n- Dynamic filtering criteria that change based on external factors\n- Content-based filtering that requires deep inspection\n- Advanced pattern matching beyond simple wildcards\n- Multi-stage filtering with intermediate logic\n\nFor AI agents: Only use script-based filtering when expression-based filtering is insufficient.\nScript filtering requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to the Script resource that contains the filtering logic. This must be a valid\nObjectId of a Script resource in this account: any other id fails the save with 422\n(\"Only a script owned by the user or a script shared by the Integration App can be used\nat filter.script._scriptId.\").\n\nThe referenced script must contain the function specified in the `function` field\nand must be written to handle filtering specifically. The script receives\nitem data as its input and must return a boolean value indicating whether\nto process the item (true) or skip it (false).\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n"},"function":{"type":"string","description":"Name of the function within the script to execute for filtering decisions. This function\nmust exist in the script referenced by _scriptId.\n\n**Function requirements**\n\nThe specified function must:\n- Accept item data as its first parameter\n- Return a boolean value (true to process the item, false to skip it)\n- Handle errors gracefully\n- Execute efficiently (as it may run for many items)\n\n**Function signature**\n\n```javascript\nfunction filterItems(itemData) {\n  // itemData contains properties of the item being evaluated\n  // Custom logic here\n  return true; // or false to skip the item\n}\n```\n\nFor AI agents: Ensure the function name exactly matches a function defined in the\nreferenced script, as mismatches will cause the filter to fail.\n"}}}}},"Transform":{"type":"object","description":"Configuration for transforming data during processing operations. This object enables\nreshaping of records.\n\n**Transformation capabilities**\n\nCeligo's transformation engine offers powerful features for data manipulation:\n- Precise field mapping with JSONPath expressions\n- Support for any level of nested arrays\n- Formula-based field value generation\n- Dynamic references to flow and integration settings\n\n**Implementation approaches**\n\nThere are two distinct transformation mechanisms available:\n\n**Rule-Based Transformation (`type: \"expression\"`)**\n- **Best For**: Most transformation scenarios from simple to complex\n- **Capabilities**: Field mapping, formula calculations, lookups, nested data handling\n- **Advantages**: Visual configuration, no coding required, intuitive interface\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear mapping requirements or need to reshape data structure\n\n**Script-Based Transformation (`type: \"script\"`)**\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Capabilities**: Full programmatic control, custom processing, complex business rules\n- **Advantages**: Maximum flexibility, can implement any transformation logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Visual transformation tools aren't sufficient for your use case\n","properties":{"type":{"type":"string","description":"Determines which transformation mechanism to use. This choice affects which properties\nmust be configured and how transformation logic is implemented.\n\n**Available types**\n\n**Rule-Based Transformation (`\"expression\"`)**\n- **Required Config**: The `expression` object with mapping definitions\n- **Behavior**: Applies declarative rules to reshape data\n- **Best For**: Most transformation scenarios from simple to complex\n- **Advantages**: Visual configuration, no coding required\n\n**Script-Based Transformation (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to transform data\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard data transformations, use `\"expression\"`\n2. For complex logic or specialized processing, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based transformations. This object enables reshaping data\nwithout requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define transformation rules that\ncan map, modify, and generate data elements.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Field mappings define how input data is transformed to target fields\n- Formulas can be used to calculate or generate new values\n- Lookups can enrich data by fetching related information\n- Mode determines how records are processed (create new or modify existing)\n","properties":{"version":{"type":"string","description":"Version of the expression format. Determines which rules\nproperty contains the transformation logic.\n","enum":["1","2"]},"rules":{"type":"array","description":"Transformation rules for version 1 expressions. An array of\nrule groups; each group is an array of field-mapping objects.\nMost transforms have a single group. Present when `version`\nis `\"1\"`. The output record contains ONLY the generated\nfields — every unmapped field is dropped (v1 has no\nequivalent of Transform 2.0's `modify` mode), and the\nrecord's trace key does not survive the rebuild.\n","items":{"type":"array","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path to read from. Supports multiple\nsyntaxes: bare field names (`id`), dot notation\n(`fulfillment.shipment_id`), slash-prefixed paths\nfor XML (`/FeedProcessingStatus`), wildcards (`*.id`,\n`*.[Internal ID]`), and array indexing (`SDF[0]`).\n"},"generate":{"type":"string","description":"Target field name to write to. Typically a bare name\n(`id`) or dot path (`SDF.Filter.ID`).\n"},"key":{"type":"string","description":"Auto-generated identifier for this rule, used by the\nUI to track individual rules for editing and reordering.\n"}},"required":["extract","generate"]}}},"rulesTwoDotZero":{"type":"object","description":"Configuration for version 2 transformation rules. This object contains the core logic\nfor how data is mapped, enriched, and transformed.\n\n**Capabilities**\n\nTransformation 2.0 provides:\n- Precise field mapping with JSONPath expressions\n- Support for deeply nested data structures\n- Formula-based field generation\n- Dynamic lookups for data enrichment\n- Multiple operating modes to fit different scenarios\n","properties":{"mode":{"type":"string","description":"Transformation mode that determines how records are handled during processing.\n\n**Available modes**\n\n**Create Mode (`\"create\"`)**\n- **Behavior**: Builds entirely new output records from inputs\n- **Use When**: Output structure differs significantly from input\n- **Advantage**: Clean slate approach, no field inheritance\n\n**Modify Mode (`\"modify\"`)**\n- **Behavior**: Makes targeted edits to existing records\n- **Use When**: Output structure should remain similar to input\n- **Advantage**: Preserves unmapped fields from the original record\n","enum":["create","modify"]},"mappings":{"$ref":"#/components/schemas/Mappings"},"lookups":{"allOf":[{"description":"Shared lookup tables used across all mappings defined in the transformation rules.\n\n**Purpose**\n\nLookups provide centralized value translation that can be referenced from any mapping\nin your transformation configuration. They enable consistent translation of codes, IDs,\nand values between systems without duplicating translation logic.\n\n**Usage in transformations**\n\nLookups are particularly valuable in transformations for:\n\n- **Data Normalization**: Standardizing values from diverse source systems\n- **Code Translation**: Converting between different coding systems (e.g., status codes)\n- **Field Enrichment**: Adding descriptive values based on ID or code lookups\n- **Cross-Reference Resolution**: Mapping identifiers between integrated systems\n\n**Implementation**\n\nLookups are defined once in this array and referenced by name in mappings:\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"statusMapping\",\n    \"map\": {\n      \"A\": \"Active\",\n      \"I\": \"Inactive\",\n      \"P\": \"Pending\"\n    },\n    \"default\": \"Unknown Status\"\n  }\n]\n```\n\nThen referenced in mappings using the lookupName property:\n\n```json\n{\n  \"generate\": \"status\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.statusCode\",\n  \"lookupName\": \"statusMapping\"\n}\n```\n\nThe system automatically applies the lookup during transformation processing.\n\nFor complete details on lookup properties and behavior, see the Lookups schema.\n"},{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the JSON shape the transformTwoDotZero processor\nevaluates `mappings[].extract` JSONPath values against at\nflow runtime. Applies only to Transform 2.0 (v2,\n`rulesTwoDotZero`); v1 transforms (the `rules` array on\n`transform.expression.rules`) and script-mode transforms\nignore this field.\n"}}}}},"script":{"type":"object","description":"Configuration for programmable script-based transformations. This object enables complex, custom\ntransformation logic beyond what expression-based transformations can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to transform data according to\nspecialized business rules or complex algorithms.\n\n**Implementation approach**\n\nScript-based transformation works by:\n1. Executing the specified function from the referenced script\n2. Passing input data to the function\n3. Using the function's return value as the transformed output\n\n**Common use cases**\n\nScript transformation is ideal for:\n- Complex business logic that can't be expressed through mappings\n- Algorithmic transformations requiring computation\n- Dynamic transformations based on external factors\n- Legacy system data format compatibility\n- Multi-stage processing with intermediate steps\n\nOnly use script-based transformation when expression-based transformation is insufficient.\nScript transformation requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to a predefined script resource containing the transformation logic.\n\nThe referenced script should contain the function specified in the\n'function' property. It must be a script in this account: any other id fails the save\nwith 422 (\"Only a script owned by the user or a script shared by the Integration App can\nbe used at transform.script._scriptId.\").\n","format":"objectid"},"function":{"type":"string","description":"Name of the function within the script to execute for transformation. This function\nmust exist in the script referenced by _scriptId.\n"}}}}},"Lookups":{"type":"array","description":"Configuration for value-to-value transformations using lookup tables.\n\n**Purpose**\n\nLookups provide a way to translate values from one system to another. They transform\ninput values into output values using either static mapping tables or\ndynamic lookup caches.\n\n**Lookup mechanisms**\n\nThere are two distinct lookup mechanisms available:\n\n1. **Static Lookups**: Define a simple key-value map object and store it as part of your resource\n   - Best for: Small, fixed sets of values that rarely change\n   - Implementation: Configure the `map` object with input-to-output value mappings\n   - Example: Country codes, status values, simple translations\n\n2. **Dynamic Lookups**: Reference an existing 'Lookup Cache' resource in your Celigo account\n   - Best for: Large datasets, frequently changing values, or complex reference data\n   - Implementation: Configure `_lookupCacheId` to reference cached data maintained independently\n   - Example: Product catalogs, customer databases, pricing information\n\n**Property usage**\n\nThere are two mutually exclusive ways to configure lookups, depending on which mechanism you choose:\n\n1. **For Static Mappings**: Configure the `map` property with a direct key-value object\n   ```json\n   \"map\": {\"US\": \"United States\", \"CA\": \"Canada\"}\n   ```\n\n2. **For Dynamic Lookups**: Configure the following properties:\n   - `_lookupCacheId`: Reference to the lookup cache resource\n   - `extract`: JSON path to extract specific value from the returned lookup object\n\n**When to use**\n\nLookups are ideal for:\n\n1. **Value Translation**: Mapping codes or IDs to human-readable values\n\n2. **Data Enrichment**: Adding related information to records during processing\n\n3. **Normalization**: Ensuring consistent formatting of values across systems\n\n**Implementation details**\n\nLookups can be referenced in:\n\n1. **Field Mappings**: Direct use in field transformation configurations\n\n2. **Handlebars Templates**: Use within templates with the syntax:\n   ```\n   {{lookup 'lookupName' record.fieldName}}\n   ```\n\n**Example usage**\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"countryCodeToName\",\n    \"map\": {\n      \"US\": \"United States\",\n      \"CA\": \"Canada\",\n      \"UK\": \"United Kingdom\"\n    },\n    \"default\": \"Unknown Country\",\n    \"allowFailures\": true\n  },\n  {\n    \"name\": \"productDetails\",\n    \"_lookupCacheId\": \"60a2c4e6f321d800129a1a3c\",\n    \"extract\": \"$.details.price\",\n    \"allowFailures\": false\n  }\n]\n```\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for the lookup table within this configuration.\n\nThis name must be unique within the scope where the lookup is defined and is used to reference\nthe lookup in handlebars templates with the syntax {{lookup 'name' value}}.\n\nChoose descriptive names that indicate the transformation purpose, such as:\n- \"countryCodeToName\" for country code to full name conversion\n- \"statusMapping\" for status code translations\n- \"departmentCodes\" for department code to name mapping\n"},"map":{"type":["object","null"],"description":"The lookup mapping table as key-value pairs. The platform stores `null`\nhere on dynamic lookups, which resolve values at runtime instead of\nfrom a static table.\n\nThis object contains the input values as keys and their corresponding\noutput values. When a input value matches a key in this object,\nit will be replaced with the corresponding value.\n\nThe map should be kept to a reasonable size (typically under 100 entries)\nfor optimal performance. For larger mapping requirements, consider using\ndynamic lookups instead.\n\nMaps can include:\n- Simple code to name conversions: {\"US\": \"United States\"}\n- Status transformations: {\"A\": \"Active\", \"I\": \"Inactive\"}\n- ID to name mappings: {\"100\": \"Marketing\", \"200\": \"Sales\"}\n\nValues can be strings, numbers, or booleans, but all are stored as strings\nin the configuration.\n"},"_lookupCacheId":{"type":"string","description":"Reference to a LookupCache resource that contains the reference data for the lookup.\n\n**Purpose**\n\nThis field connects the lookup to an external data source that has been cached in the system.\nUnlike static lookups that use the `map` property, dynamic lookups can reference large datasets\nor frequently changing information without requiring constant updates to the integration.\n\n**Implementation details**\n\nThe LookupCache resource referenced by this ID contains:\n- The data records to be used as a reference source\n- Configuration for how the data should be indexed and accessed\n- Caching parameters to balance performance with data freshness\n\n**Usage patterns**\n\nCommonly used to reference:\n- Product catalogs or SKU databases\n- Customer or account information\n- Pricing tables or discount rules\n- Complex business logic lookup tables\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n","format":"objectid"},"extract":{"type":"string","description":"JSON path expression that extracts a specific value from the cached lookup object.\n\n**Purpose**\n\nWhen using dynamic lookups with a LookupCache, this JSON path identifies which field to extract\nfrom the cached object after it has been retrieved using the lookup key.\n\n**Implementation details**\n\n- Must use JSON path syntax (similar to mapping extract fields)\n- Operates on the cached object returned by the lookup operation\n- Examples:\n  - \"$.name\" - Extract the name field from the top level\n  - \"$.details.price\" - Extract a nested price field\n  - \"$.attributes[0].value\" - Extract a value from the first element of an array\n\n**Usage scenario**\n\nWhen a lookup cache contains complex objects:\n```json\n// Cache entry for key \"PROD-123\":\n{\n  \"id\": \"PROD-123\",\n  \"name\": \"Premium Widget\",\n  \"details\": {\n    \"price\": 99.99,\n    \"currency\": \"USD\",\n    \"inStock\": true\n  }\n}\n```\n\nSetting extract to \"$.details.price\" would return 99.99 as the lookup result.\n\nIf no extract is provided, the entire cached object is returned as the lookup result.\n"},"default":{"type":["string","null"],"description":"Default value to use when the source value is not found in the lookup map.\nThe platform stores `null` here when no default is configured.\n\nThis value is used as a fallback when:\n1. The source value doesn't match any key in the map\n2. allowFailures is true or omitted\n\nSetting an appropriate default helps prevent flow failures due to unexpected\nvalues and provides predictable behavior for edge cases.\n\nCommon default patterns include:\n- Descriptive unknowns: \"Unknown Country\", \"Unspecified Status\"\n- Original value indicators: \"{Original Value}\", \"No mapping found\"\n- Neutral values: \"Other\", \"N/A\", \"Miscellaneous\"\n\nA save refuses a default together with an explicit `allowFailures: false`\n(422 `Lookups with default set can not have allowFailures explicitly set to\nfalse.`): a lookup either fails a miss (`allowFailures: false`, no default) or\nfalls back to its default.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, missing lookup values will use the default value rather than causing an error.\n\n**Behavior control**\n\nThis field determines how the system handles source values that don't exist in the map:\n\n- true: Use the default value for missing mappings and continue processing\n- false: Treat missing mappings as errors, failing the record (`lookup_error` for a\n  static map, `lookupcache_evaluation_failed` for a lookup cache)\n- omitted or null: the record doesn't fail either. On an import a static lookup writes\n  its default if it has one, and a static or cache lookup without one leaves the\n  mapped field out of the output, with no error\n\n**Recommendation**\n\nSet this to true when:\n- New source values might appear over time\n- Data quality issues could introduce unexpected values\n- Processing should continue even with imperfect mapping\n\nSet this to false when:\n- Complete data accuracy is critical\n- All possible source values are known and controlled\n- Missing mappings indicate serious data problems that should be addressed\n\nA default raises no error: records that miss go out with the default value and\nnothing reports them. Use true only when that fallback is acceptable at the\ndestination, and set false explicitly when a missing value must stop the record.\n"}}}},"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: one in the script named by `_scriptId`, or one the code deployed on\nthe stack named by `_stackId` exposes."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`. Send this or `_stackId`, not both.\nMust be a script in this account: any other id fails the save with 422, naming the hook,\nsuch as \"Only a script owned by the user or a script shared by the Integration App can\nbe used at hooks.preMap._scriptId.\" on an import's `preMap`."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments.\nThe API doesn't enforce the either/or: a save carrying both `_scriptId` and `_stackId`\nanswers 200 and stores both, and the script and the stack then each list the step as a\ndependent."},"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."}}},"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"},"PreviewCompareResult":{"type":"object","description":"Result of running a single AI agent configuration against the input data.","properties":{"statusCode":{"type":"integer","description":"HTTP-style status code for this entry. 200 on success, 422 on configuration or provider error."},"_text":{"type":["string","null"],"description":"Free-form text response from the LLM. Present when `output.format.type` is `text`."},"_json":{"type":"object","additionalProperties":true,"description":"Structured JSON response from the LLM. Populated when `output.format.type` is `json_schema`."},"errors":{"type":"array","description":"Errors encountered during execution. Present when `statusCode` is not 200.","items":{"type":"object","properties":{"field":{"type":"string","description":"Dot path of the import field that failed validation. Present on per-entry validation errors."},"code":{"type":"string","description":"Machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"source":{"type":"string","description":"Component that produced the error."},"occurredAt":{"type":"number","description":"Epoch milliseconds when the error occurred."},"stage":{"type":"string","description":"Pipeline stage where the error occurred."},"resolved":{"type":"boolean","description":"When true, the error has been marked as resolved."},"classification":{"type":"string","description":"Classification label assigned to the error."}}}},"usage":{"type":"object","description":"Token usage and cost breakdown for the LLM call.","properties":{"input_tokens":{"type":"integer","description":"Tokens consumed by the prompt."},"output_tokens":{"type":"integer","description":"Tokens generated in the response."},"total_tokens":{"type":"integer","description":"Sum of input and output tokens."},"input_tokens_details":{"type":"object","description":"Breakdown of how the input tokens were consumed.","properties":{"cached_tokens":{"type":"integer","description":"Tokens served from the provider's prompt cache."}}},"output_tokens_details":{"type":"object","description":"Breakdown of how the output tokens were generated.","properties":{"reasoning_tokens":{"type":"integer","description":"Tokens used for chain-of-thought reasoning."}}},"service_tier":{"type":"string","description":"Service tier used for the request."},"model":{"type":"string","description":"Exact model version used by the provider."},"total_cost":{"type":"number","description":"Estimated cost in USD for this call."},"image_generation_details":{"type":["object","null"],"description":"Image generation usage details, if applicable."},"web_search_details":{"type":["object","null"],"description":"Web search usage details, if applicable.","properties":{"tool_type":{"type":"string","description":"Type of web search tool the provider invoked."},"billed_search_actions":{"type":"integer","description":"Number of web search actions billed for the request."},"model":{"type":"string","description":"Model used for the web search calls."},"service_tier":{"type":"string","description":"Service tier used for the web search calls."}}},"function_tool_details":{"type":["object","null"],"description":"Function tool usage details, if applicable."},"mcp_tool_details":{"type":["object","null"],"description":"MCP tool usage details, if applicable."},"token_only_raw_cost":{"type":"number","description":"Token-only cost before platform markup."},"token_only_ai_credits":{"type":"number","description":"AI credits consumed by token usage."}}},"timing":{"type":"object","description":"Execution timing.","properties":{"total_request_time":{"type":"number","description":"Total wall-clock time in seconds for the LLM call."}}}}},"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/imports/preview/compare":{"post":{"operationId":"previewCompareAiAgents","tags":["AI Agents"],"summary":"Preview and compare AI agent configurations","description":"Runs one or more AI agent import configurations against the same input\ndata and returns the LLM response, token usage, and timing for each.\nNo job is created and no state is updated — this is a stateless preview.\n\nThe UI uses this to compare model or prompt variations side-by-side\nbefore saving. Each entry in `imports` is a full import object (the same\nshape returned by `GET /v1/imports/{_id}`) with the `aiAgent` block\nconfigured for the variant being tested.\n\nEach entry in `imports` must include `adaptorType: \"AiAgentImport\"` and\na valid `aiAgent` configuration. A `_connectionId` is required unless the\nagent is on a built-in provider (`openai`, `gemini`, `anthropic`) using\nplatform-managed credentials — catalog providers always need one, since\nthe preview runs the model. The response array is positional\n— `response[i]` corresponds to `imports[i]`. A per-entry\n`statusCode: 422` with `errors` means that specific configuration failed,\nnot that the overall request failed.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewCompareRequest"}}}},"responses":{"200":{"description":"Array of results, one per import entry. Each result contains the LLM\nresponse text, structured output, token usage, and timing.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PreviewCompareResult"}}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"422":{"description":"One or more import entries failed validation — e.g. a required\n`aiAgent` field is missing. The body is the same positional results\narray as a 200 response; each failing entry carries `statusCode: 422`\nand an `errors` array instead of LLM output.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PreviewCompareResult"}}}}}}}}}}
````

## List dependencies of an AI agent

> Returns resources that depend on this AI agent import. Keys are resource\
> types (e.g. \`flows\`); values are arrays of dependency entries.\
> \
> Check dependencies before deleting — the DELETE endpoint is blocked if\
> dependents exist. Returns \`{}\` for both zero-dependency and nonexistent IDs.

```json
{"openapi":"3.2.0","info":{"title":"AI Agents","version":"1.0.0"},"tags":[{"name":"AI Agents","description":"AI agents are LLM-powered import steps that classify, extract, validate, or\ngenerate data within integration flows. They are stored as import resources\nwith `adaptorType: AiAgentImport` and managed via the `/v1/imports` endpoints.\n\nThree built-in providers — OpenAI (via the Responses API), Google Gemini (via\nLiteLLM), and Anthropic Claude (via LiteLLM) — run on platform-managed\ncredentials or, with a `_connectionId`, on your own key (BYOK). Catalog\nproviders — Mistral, xAI, Hugging Face, DeepSeek, Cohere, and Groq — use one generic\nflat configuration format and always run on a BYOK connection.\n\n## AI agent schema\n\n{% openapi-schemas spec=\"ai-agent\" schemas=\"AiAgent\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"DependencyResponse":{"type":"object","description":"Map of dependent-resource types to arrays of dependency entries.\nKeys are plural resource type strings (e.g. `flows`, `imports`,\n`connections`). An empty object `{}` means no dependents.\n","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/DependencyEntry"}}},"DependencyEntry":{"type":"object","description":"A single resource that depends on the queried resource.","properties":{"id":{"type":"string","description":"Unique identifier of the dependent resource."},"name":{"type":"string","description":"Display name of the dependent resource."},"type":{"type":"string","description":"Sub-type of the dependent resource when its collection distinguishes one, such as\n`stream` for a listener export. Only present for resource types that carry a `type`."},"paths":{"type":"array","description":"Dot-notation paths within the dependent resource that reference\nthe target resource. `[*]` denotes array elements.","items":{"type":"string"}},"accessLevel":{"type":"string","description":"The caller's access level on the dependent resource."},"dependencyIds":{"type":"object","description":"Map of resource types to arrays of IDs that this dependent\nresource references on the target. Keys are singular or plural\nresource type strings; values are arrays of ID strings.","additionalProperties":{"type":"array","items":{"type":"string"}}}},"required":["id","name","paths","accessLevel","dependencyIds"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}}}},"paths":{"/v1/imports/{_id}/dependencies":{"get":{"operationId":"listAiAgentDependencies","tags":["AI Agents"],"summary":"List dependencies of an AI agent","description":"Returns resources that depend on this AI agent import. Keys are resource\ntypes (e.g. `flows`); values are arrays of dependency entries.\n\nCheck dependencies before deleting — the DELETE endpoint is blocked if\ndependents exist. Returns `{}` for both zero-dependency and nonexistent IDs.","parameters":[{"name":"_id","in":"path","required":true,"description":"Import resource ID.","schema":{"type":"string","format":"objectId"}}],"responses":{"200":{"description":"Dependency map. Returns `{}` when no dependents exist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DependencyResponse"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"}}}}}}
```

## Chat with a saved AI agent

> Sends one message to a saved AI agent and streams the answer as\
> \`text/event-stream\`. Events arrive in order: \`init\`, \`thinking\`\
> (started), a \`progress\` pair per tool call (\`tool \<name> started\` /\
> \`finished\`), \`thinking\` (completed) — a Celigo Tool's inner-step\
> \`progress\` events can still arrive after it — \`responsechunk\` deltas\
> of the reply (raw text — join them), \`usage\` (tokens and AI-credit cost),\
> \`complete\` (\`data.success\`); an \`error\` event replaces the reply when\
> the turn fails (see \`ChatStreamEvent\`). The agent runs as configured:\
> its tools (web search, MCP servers, Celigo Tools) execute for real and\
> the turn is billed — AI credits on a hosted model, the key's owner on\
> a BYOK one. The same \`conversationHistoryId\` across requests keeps a\
> conversation; without it the message is a single turn and nothing is\
> stored. Needs the \`ai\_agent:chat\` permission (manage and administrator\
> roles; monitor gets 403) and \`Content-Type: application/json\` (415 if\
> not).\
> \
> None of the flow pipeline runs — no input filter, mappings, hooks or\
> response mapping — so this is the agent in conversation, not the import\
> step; \`POST /v1/imports/preview\` runs the step with its pre-submit\
> stages. A tile-level manager can chat only with agents inside\
> accessible integrations. Files travel as storage keys from the UI's\
> signed-upload flow; the public API has no upload route. The stream\
> carries its own failure: a turn the agent service could not finish\
> still answers 200 with an \`error\` event inside it. Read the events in\
> order and stop at \`complete\`, or at an \`error\` that ends the stream.

```json
{"openapi":"3.2.0","info":{"title":"AI Agents","version":"1.0.0"},"tags":[{"name":"AI Agents","description":"AI agents are LLM-powered import steps that classify, extract, validate, or\ngenerate data within integration flows. They are stored as import resources\nwith `adaptorType: AiAgentImport` and managed via the `/v1/imports` endpoints.\n\nThree built-in providers — OpenAI (via the Responses API), Google Gemini (via\nLiteLLM), and Anthropic Claude (via LiteLLM) — run on platform-managed\ncredentials or, with a `_connectionId`, on your own key (BYOK). Catalog\nproviders — Mistral, xAI, Hugging Face, DeepSeek, Cohere, and Groq — use one generic\nflat configuration format and always run on a BYOK connection.\n\n## AI agent schema\n\n{% openapi-schemas spec=\"ai-agent\" schemas=\"AiAgent\" 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":{"ChatRequest":{"type":"object","description":"Body of a chat turn with a saved AI agent: `data` holds the one message\n— its `text`, its `files` (name and storage `blobKey` of uploaded\nfiles), and an optional `conversationHistoryId` that keeps a\nconversation across requests.","required":["data"],"properties":{"data":{"type":"array","minItems":1,"maxItems":1,"description":"Exactly one message per request. A request with more than one entry\nis rejected before the agent runs.","items":{"$ref":"#/components/schemas/ChatMessage"}}}},"ChatMessage":{"type":"object","description":"One message for the agent. Give it `text`, `files`, or both; the agent\nanswers in the streamed response.","properties":{"text":{"type":"string","description":"The message the agent answers. Whitespace-only text counts as empty,\nso a message with no `files` must carry visible text."},"files":{"type":"array","description":"Files the agent reads with the message (images, PDFs, JSON documents).\nEach entry names an object already uploaded to the account's file\nstorage through the signed-upload flow the integrator.io UI uses; the\npublic API has no upload route of its own.","items":{"type":"object","required":["name","blobKey"],"properties":{"name":{"type":"string","description":"File name shown to the agent."},"blobKey":{"type":"string","description":"Storage key of the uploaded file, as returned by the signed-upload flow."}}}},"conversationHistoryId":{"type":"string","maxLength":200,"description":"Caller-chosen key for a conversation with this agent. Turns sent\nwith the same key share a history: the agent sees the earlier\nrequests and replies and the history is updated after each turn.\nThe key is scoped to the calling user and the agent, so the same\nstring against a different agent is a different conversation. Omit\nit for a single-turn message — nothing is stored."}}},"ChatStreamEvent":{"type":"object","description":"One server-sent event of a chat turn. The stream is `text/event-stream`:\neach event is an `event:` line naming the event and one or more `data:`\nlines carrying its payload, separated by a blank line. `data` is JSON for\nevery event except `responsechunk`, whose `data:` lines are raw text (one\nline per line of the delta). A successful turn is `init`, `thinking`\n(started), any number of `progress` events, `thinking` (completed), the\n`responsechunk` deltas, `usage`, `complete`; a Celigo Tool's inner-step\n`progress` events can still arrive after `thinking` (completed).","required":["event"],"properties":{"event":{"type":"string","enum":["init","thinking","progress","responsechunk","bloboutput","usage","complete","error"],"description":"Name carried on the `event:` line."},"data":{"description":"The event payload. For `error` the payload shape says where the\nfailure happened. The agent service reports `{code, message, source?}`\n(for example `tool_stream_error` / `Tool execution failed` with\n`source: tools`) and the stream goes on to `usage` and `complete`\nwith `success: false`. The chat relay reports `{content: {code,\nmessage}}` and ends the stream there — no `usage`, no `complete`\n(`validation_error` for a `conversationHistoryId` over 200\ncharacters). A stream that broke before reaching the relay\narrives as a `data:` line with no `event:` line whose JSON carries\n`error` or `code`.","oneOf":[{"type":"object","additionalProperties":true},{"type":"string"}]}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"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"}}}}}},"paths":{"/v1/imports/{_id}/aiagents/chat":{"post":{"operationId":"chatWithAiAgent","tags":["AI Agents"],"summary":"Chat with a saved AI agent","description":"Sends one message to a saved AI agent and streams the answer as\n`text/event-stream`. Events arrive in order: `init`, `thinking`\n(started), a `progress` pair per tool call (`tool <name> started` /\n`finished`), `thinking` (completed) — a Celigo Tool's inner-step\n`progress` events can still arrive after it — `responsechunk` deltas\nof the reply (raw text — join them), `usage` (tokens and AI-credit cost),\n`complete` (`data.success`); an `error` event replaces the reply when\nthe turn fails (see `ChatStreamEvent`). The agent runs as configured:\nits tools (web search, MCP servers, Celigo Tools) execute for real and\nthe turn is billed — AI credits on a hosted model, the key's owner on\na BYOK one. The same `conversationHistoryId` across requests keeps a\nconversation; without it the message is a single turn and nothing is\nstored. Needs the `ai_agent:chat` permission (manage and administrator\nroles; monitor gets 403) and `Content-Type: application/json` (415 if\nnot).\n\nNone of the flow pipeline runs — no input filter, mappings, hooks or\nresponse mapping — so this is the agent in conversation, not the import\nstep; `POST /v1/imports/preview` runs the step with its pre-submit\nstages. A tile-level manager can chat only with agents inside\naccessible integrations. Files travel as storage keys from the UI's\nsigned-upload flow; the public API has no upload route. The stream\ncarries its own failure: a turn the agent service could not finish\nstill answers 200 with an `error` event inside it. Read the events in\norder and stop at `complete`, or at an `error` that ends the stream.","parameters":[{"name":"_id","in":"path","required":true,"description":"The AI agent — an import with `adaptorType: AiAgentImport`.","schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatRequest"}}}},"responses":{"200":{"description":"The turn as a stream of server-sent events, ending with `complete`.\nA turn the agent service could not finish still answers 200: the\nfailure arrives inside the stream as an `error` event.","content":{"text/event-stream":{"itemSchema":{"$ref":"#/components/schemas/ChatStreamEvent"}}}},"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"},"415":{"description":"`Content-Type: application/json` header is missing."}}}}}}
```

## Chat with an unsaved AI agent document

> Sends one message to an AI agent document carried in the request — a\
> draft never saved, or a saved agent with unsaved changes — and streams\
> the answer back as \`text/event-stream\` with the same events as\
> \`POST /v1/imports/{\_id}/aiagents/chat\` (see \`ChatStreamEvent\`). The\
> document is validated in memory the way a save validates it and is\
> never persisted: the saved agent, if there is one, is untouched, so\
> this is how a change is tried before it is saved. The agent's tools\
> execute for real and the turn is billed; none of the flow pipeline\
> runs. \`conversationHistoryId\` keeps a conversation keyed on the\
> document's \`\_id\` when one is present, so a draft of a saved agent can\
> continue that agent's history. Needs the \`ai\_agent:virtual\_chat\`\
> permission (manage and administrator roles; monitor gets 403) and\
> \`Content-Type: application/json\` (415 otherwise).\
> \
> The integrator.io agent editor uses this route for a Test or chat\
> message while the form has unsaved edits. A tile-level manager can use\
> it only when every connection, MCP connection and Celigo Tool the\
> document references is inside accessible integrations. A turn the\
> agent service could not finish still answers 200 with an \`error\` event\
> inside the stream.

````json
{"openapi":"3.2.0","info":{"title":"AI Agents","version":"1.0.0"},"tags":[{"name":"AI Agents","description":"AI agents are LLM-powered import steps that classify, extract, validate, or\ngenerate data within integration flows. They are stored as import resources\nwith `adaptorType: AiAgentImport` and managed via the `/v1/imports` endpoints.\n\nThree built-in providers — OpenAI (via the Responses API), Google Gemini (via\nLiteLLM), and Anthropic Claude (via LiteLLM) — run on platform-managed\ncredentials or, with a `_connectionId`, on your own key (BYOK). Catalog\nproviders — Mistral, xAI, Hugging Face, DeepSeek, Cohere, and Groq — use one generic\nflat configuration format and always run on a BYOK connection.\n\n## AI agent schema\n\n{% openapi-schemas spec=\"ai-agent\" schemas=\"AiAgent\" 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":{"VirtualChatRequest":{"type":"object","description":"Body of a chat turn with an AI agent document that has not been saved\n(or has unsaved changes): `data` holds the one message — `text`,\n`files` and an optional `conversationHistoryId`, as on the saved-agent\nroute — and `import` carries the document to run, in the shape\n`GET /v1/imports/{_id}` returns. The document is validated in memory\nthe same way a save validates it and is never persisted.","required":["data","import"],"properties":{"data":{"type":"array","minItems":1,"maxItems":1,"description":"Exactly one message per request.","items":{"$ref":"#/components/schemas/ChatMessage"}},"import":{"description":"The AI agent document to run, with `adaptorType: AiAgentImport` and\nthe `aiAgent` block for the configuration under test. An `_id` is\noptional; when present it is kept, so a `conversationHistoryId`\ncontinues the same history the saved agent uses. A `_connectionId`\nselects the BYOK connection the turn runs through.","allOf":[{"$ref":"#/components/schemas/Request"}]}}},"ChatMessage":{"type":"object","description":"One message for the agent. Give it `text`, `files`, or both; the agent\nanswers in the streamed response.","properties":{"text":{"type":"string","description":"The message the agent answers. Whitespace-only text counts as empty,\nso a message with no `files` must carry visible text."},"files":{"type":"array","description":"Files the agent reads with the message (images, PDFs, JSON documents).\nEach entry names an object already uploaded to the account's file\nstorage through the signed-upload flow the integrator.io UI uses; the\npublic API has no upload route of its own.","items":{"type":"object","required":["name","blobKey"],"properties":{"name":{"type":"string","description":"File name shown to the agent."},"blobKey":{"type":"string","description":"Storage key of the uploaded file, as returned by the signed-upload flow."}}}},"conversationHistoryId":{"type":"string","maxLength":200,"description":"Caller-chosen key for a conversation with this agent. Turns sent\nwith the same key share a history: the agent sees the earlier\nrequests and replies and the history is updated after each turn.\nThe key is scoped to the calling user and the agent, so the same\nstring against a different agent is a different conversation. Omit\nit for a single-turn message — nothing is stored."}}},"Request":{"type":"object","description":"Configuration for an AI agent import. Set `aiAgent.provider` to choose\nthe LLM backend, then configure the matching format: `aiAgent.openai` for\nOpenAI, `aiAgent.litellm` for Gemini and Anthropic Claude, or the generic\nflat format (`aiAgent.model`, `aiAgent.modelOptions`, …) for the catalog\nproviders. Omit `_connectionId` to use platform-managed credentials on a\nbuilt-in provider; catalog providers need a BYOK connection to run.","required":["name","adaptorType","aiAgent"],"allOf":[{"$ref":"#/components/schemas/AiAgentBase"}]},"AiAgentBase":{"type":"object","description":"Writable AI agent import fields shared by the request and response schemas. Set\n`aiAgent.provider` to choose the LLM backend, then configure the matching format:\n`aiAgent.openai` for OpenAI, `aiAgent.litellm` for Gemini and Anthropic Claude\n(with provider-specific settings under `aiAgent.litellm._overrides`), or the\ngeneric flat format (`aiAgent.model`, `aiAgent.modelOptions`, …) for the catalog\nproviders Mistral, xAI, Hugging Face, DeepSeek, Cohere, and Groq.","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Display name for the AI agent."},"description":{"type":"string","description":"Optional description of the AI agent's purpose."},"_connectionId":{"type":"string","format":"objectId","description":"Connection for BYOK (bring your own key) — an HTTP connection to the vendor's API.\nOptional for the built-in providers (`openai`, `gemini`, `anthropic`), which run on\nplatform-managed credentials when it is omitted. Catalog providers (`mistral`, `xai`,\n`huggingface`, `deepseek`, `cohere`, `groq`) have no platform-managed credentials: the save\nrequires it — without one the agent fails to save with 422 `connection_required` on\n`_connectionId`."},"_integrationId":{"type":"string","format":"objectId","description":"Integration this AI agent belongs to. Omitted for a standalone agent."},"adaptorType":{"type":"string","enum":["AiAgentImport"],"description":"Must be `AiAgentImport`."},"_connectorId":{"type":"string","format":"objectId","description":"Connector this AI agent was created from, set when it is part of an installed integration app."},"aiAgent":{"$ref":"#/components/schemas/AiAgentConfig"},"mappings":{"allOf":[{"$ref":"#/components/schemas/Mappings"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Selects the data context that `mappings[].extract` evaluates against. With no\nmappings the agent receives the entire record as JSON (or only the record's own\ntop-level `text`, `record` or `files` fields, when it has any) and `inputContext`\nis moot. `record`\n(the default) extracts against the flat record; `envelope` extracts against the\nruntime envelope, which exposes the record alongside job, settings, and other\nrun-time metadata (`$.record.field`, `$.settings.field`, etc.). Same contract as\nthe import resource's `inputContext`."},"filter":{"description":"Filter applied to incoming records before they are sent to the LLM. Records that match\ncontinue through the agent; records that don't skip it, counted as ignored with no model\ncall, and continue to the next step.","allOf":[{"$ref":"#/components/schemas/Filter"}]},"responseTransform":{"description":"Transformation that reshapes the LLM's response after records are processed, before the\nresponse is handled by response mappings and downstream steps.","allOf":[{"$ref":"#/components/schemas/Transform"}]},"hooks":{"type":"object","description":"Custom JavaScript hooks that run at fixed points in the agent's import lifecycle.\nAI agents do not aggregate records, so the `postAggregate` hook that other import\ntypes expose is intentionally omitted here.","properties":{"preMap":{"description":"Hook that runs on each page of records before the agent's mappings are applied.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postMap":{"description":"Hook that runs after mappings are applied, before records are sent to the LLM.","allOf":[{"$ref":"#/components/schemas/Hook"}]},"postSubmit":{"description":"Hook that runs after the LLM responds, with both the submitted records and the responses.","allOf":[{"$ref":"#/components/schemas/Hook"}]}}},"oneToMany":{"$ref":"#/components/schemas/OneToMany"},"pathToMany":{"$ref":"#/components/schemas/PathToMany"},"distributed":{"type":"boolean","description":"Always false for AI agent imports — the agent executes on Celigo's servers, not inside a target application."},"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 are silently skipped. Rarely meaningful for AI agents; accepted for import-surface parity."},"ignoreMissing":{"type":"boolean","description":"When true, records that do not already exist in the destination are silently skipped. Rarely meaningful for AI agents; accepted for import-surface parity."},"mockResponse":{"type":"array","description":"Predefined response records used in place of calling the LLM when testing the agent,\nso flows can run without spending tokens. Records must be in integrator.io canonical\nformat — 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 for structured-output agents."},"_text":{"type":"string","description":"Mock response body for text-output agents."},"_headers":{"type":"object","description":"Mock response headers."},"blobKey":{"type":"string","description":"Mock output of an agent with `blob` output (image generation:\n`openai.output.format.type` or `litellm.responseFormat.type` set to `blob`),\nwhich returns the stored image's key here. Unrelated to the import-level `blob`\nflag, which agents refuse."}}}}}},"AiAgentConfig":{"type":"object","description":"AI agent configuration for an AiAgentImport. An `ai_agent` guardrail is configured by\nthe guardrail spec's `GuardrailAiAgentConfig` instead, which has no `tools` arrays.\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 the\nsave requires one — without it the agent fails to save with 422\n`connection_required` on `_connectionId`.\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","max"],"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":"Caps the length of the model's response, in tokens. An agent created without it is\nsaved with 5000; raise it for long outputs such as large extractions."},"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":"Which editor the UI opens `jsonSchema` in; the schema sent to the provider\nis the same either way, and the field is only meaningful when `type` is\n\"json_schema\". Unset opens the visual editor, and an edit there rebuilds\n`jsonSchema` from the field list, keeping only `type`, `properties`,\n`required`, `items`, `description`, `enum`, `pattern`, length and numeric\nbounds, `multipleOf`, `minItems`/`maxItems` and a `\"null\"` type — other\nkeywords are dropped without a prompt, and `integer` becomes `number`."},"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`."}}},"schema":{"type":"object","readOnly":true,"additionalProperties":true,"description":"Server-written copy of `jsonSchema`, returned whenever `jsonSchema` is set — on\nAI agent imports and on guardrail agents alike. Send `jsonSchema`, not this field."}},"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","code_execution","tool"],"description":"Type of tool. Every type except `code_execution` is configured through the\nsibling object of the same name; OpenAI spells this one `code_execution`, while\nthe Gemini and Anthropic overrides spell theirs `codeExecution` — the other spelling\nfails with 422 `enum`."},"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"]}}}},"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":"Which editor the UI opens `jsonSchema` in; the schema sent to the provider is\nthe same either way, and the field is only meaningful when `type` is\n\"json_schema\". Unset opens the visual editor, and an edit there rebuilds\n`jsonSchema` from the field list, keeping only `type`, `properties`, `required`,\n`items`, `description`, `enum`, `pattern`, length and numeric bounds,\n`multipleOf`, `minItems`/`maxItems` and a `\"null\"` type — other keywords are\ndropped without a prompt, and `integer` becomes `number`."},"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","codeExecution","mcp","tool"],"description":"Type of Gemini tool. `codeExecution` is the Gemini spelling — `code_execution`\nfails with 422 `enum`."},"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"]}}}},"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","codeExecution"],"description":"Type of Claude tool. `codeExecution` is the Anthropic spelling — `code_execution`\nfails with 422 `enum`."},"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). Each\nentry is either a plain tool name or an object carrying display\nmetadata — same contract as the OpenAI and Gemini `allowedTools`.","items":{"oneOf":[{"title":"name","type":"string","description":"Tool name as exposed by the MCP server."},{"title":"tool","type":"object","required":["name"],"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."}}}]}}}},"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."}}}}}}}}},"Mappings":{"type":"array","description":"Array of field mapping configurations for transforming data from one format into another.\n\n**Guidance**\n\nThis schema is designed around RECURSION as its core architectural principle. Understanding this recursive\nnature is essential for building effective mappings:\n\n1. The schema is self-referential by design - a mapping can contain nested mappings of the same structure\n2. Complex data structures (nested objects and arrays of objects, to any depth) are ALL\n   handled through this recursive pattern; an array of arrays is not (build one in a script hook)\n3. Each mapping handles one level of the data structure; deeper levels are handled by nested mappings\n\nWhen generating mappings programmatically:\n- For simple fields (string, number, boolean): Create single mapping objects\n- For objects: Create a parent mapping with nested 'mappings' array containing child field mappings\n- For arrays: Use 'buildArrayHelper' with extract paths defining array inputs and\n  recursive 'mappings' to define object structures\n\nThe system will process these nested structures recursively during runtime, ensuring proper construction\nof complex hierarchical data while maintaining excellent performance.\n\nOn save, a leaf row with neither a source (`extract`, `hardCodedValue`, `lookupName`) nor a\ndestination (`generate`) is dropped silently, at every nesting level, and so is an object or\nobject-array row left with no source, no destination and no remaining child `mappings` or\n`buildArrayHelper` entries; a row with only one of the two, and a source-less, destination-less\nwrapper that still has children, are kept.\n\nThe shape of the array is the mapper's **JSON structure**. Named top-level items produce an object\nrecord (`record { }`). Exactly one top-level item with `dataType: objectarray` and no `generate` is\nthe `rows [ ]` structure: the mapped record is an array — usually one element per element of the\nsource array that root's `buildArrayHelper` iterates — Handlebars templates such as `http.body`\nbind it as `rows` instead of `record`, and the mapping editor shows that row's destination disabled.\n","items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}}},"items":{"type":"object","properties":{"generate":{"type":"string","description":"**Purpose**\nDefines the target field name in the output object/record.\n\n**Guidance**\nThis is the PRIMARY FIELD that identifies the output property being created:\n\n- For regular fields: Set to the exact property name (e.g., \"firstName\", \"price\", \"isActive\")\n- For object fields: Set to the object property name, then add child mappings in the 'mappings' array\n- For array fields: Set to the array property name, then configure 'buildArrayHelper'\n- One structural blank: the single top-level `objectarray` item of a `rows [ ]` mapping (the\n  array output structure — see the array's description). A child-bearing wrapper and a source-only row that\n  already exist in a saved document also stay unnamed when the tree is re-sent — filling in a\n  name changes the output; it does not complete the row\n\nIMPORTANT: Do NOT use dot notation (e.g., \"customer.firstName\") in this field. Instead, create proper\nhierarchical structure with nested mappings:\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"status\": \"Active\",\n  \"mappings\": [\n    {\"generate\": \"firstName\", \"dataType\": \"string\", \"extract\": \"$.name.first\", \"status\": \"Active\"}\n  ]\n}\n```\n\nWhen parsing existing mappings, an empty `generate` on a row inside an `arrayarray` row's\n`buildArrayHelper` entry is that row's inner array (`arrayarray` fails at run time; see\n`dataType`); other unnamed rows are the wrappers and source-only rows described above; an\nempty `generate` on the ONLY top-level item, with `dataType: objectarray`, is the `rows [ ]` JSON\nstructure — array output, bound as `rows` in templates — and is intentional, not a missing name.\n"},"dataType":{"type":"string","description":"**Purpose**\nExplicitly declares the data type of the output field, controlling how data is processed and structured.\n\n**Guidance**\nThis is a REQUIRED field that fundamentally determines mapping behavior:\n\n**Simple Types (direct value mapping)**\n- `string`: Text values, converts other types to string representation\n- `number`: Numeric values. Converts a numeric string (`\"24\"` → `24`,\n  `\"007\"` → `7`) and leaves the field out for an empty string; a value\n  holding letters or a thousands separator (`\"1,250.00\"`) fails the\n  record with `non_parsable_string` (`<generate>: String contains alpha\n  characters`)\n- `boolean`: True/false values, converts truthy/falsy values\n\nDates are represented as strings — use `string` for date fields and\ndrive the parsing/formatting through the `extractDateFormat` /\n`generateDateFormat` / `extractDateTimezone` / `generateDateTimezone`\nfields. There is no separate `date` enum value.\n\n**Complex Types (require additional configuration)**\n- `object`: Creates a nested object from child mappings in the 'mappings' array, or\n  copies a whole object with `extract` (one or the other, not both)\n\n**Array Types**\n- `stringarray`: Array of strings\n- `numberarray`: Array of numbers\n- `booleanarray`: Array of booleans\n- `objectarray`: Array of objects (most common array type)\n\n`arrayarray` is not a type the mapper recognizes: a save accepts it, but every record\nthen fails with `Destination datatype is a required field.` Build an array of arrays\nin a script hook instead.\n\nArray dataTypes are populated two ways: construct or iterate through\n`buildArrayHelper` entries, or set a constant with `hardCodedValue`. The\nrow's own `extract` is never read on an array type: a primitive-array row\nthat has only an `extract` (JSONPath, Handlebars or a literal) yields\nnothing, with no error, and an `objectarray` row without `buildArrayHelper`\nor `hardCodedValue` is refused with 422 (\"buildArrayHelper\" or\n\"hardCodedValue\" must be present for object array datatype.). To copy a\nsource array through unchanged, use one entry that iterates it\n(`buildArrayHelper: [{\"extract\": \"$.items[*]\"}]`).\n\nIMPORTANT: The dataType controls which additional fields are relevant:\n- For date-like string fields: extractDateFormat, generateDateFormat, etc. become relevant\n- For object types: 'mappings' array becomes relevant\n- For array types: `buildArrayHelper` or `hardCodedValue` (see above)\n\nWhen analyzing existing mappings or generating new ones, always check dataType first\nto understand what additional fields should be present.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray"]},"extract":{"type":"string","description":"**Purpose**\nDefines how to retrieve data from the input record to populate the output field.\n\n**Guidance**\nThis field supports THREE DISTINCT PATTERNS that are easily distinguished:\n\n**1. json Path Syntax**\n- MUST start with '$' — the record root. An object record is addressed as\n  '$.field'; a grouped (array) record as '$[0].field' / '$[*].field'\n- Used for precisely targeting data in structured JSON objects\n- Examples: '$.customer.firstName', '$.items[0].price', '$.addresses[*].street',\n  '$[*].Tax' (every row of a grouped record)\n- Wildcards like [*] extract multiple values/objects\n- A key that contains a dot needs bracket notation: `$['P.O. Number']` (or\n  `$[\"P.O. Number\"]`, `$.[P.O. Number]`). `$.P.O. Number` is read as a nested\n  path: it maps nothing and the record still succeeds. Spaces, hyphens and `#`\n  need no brackets (`$.Ship To Name`, `$.Tracking #`)\n- Filter expressions such as `$.items[?(@.active)]` fail the record with\n  \"Eval [?(expr)] prevented in JSONPath expression.\"\n\n```json\n\"extract\": \"$.customer.addresses[*]\"  // Extracts all addresses\n```\n\n**2. Handlebars Template Syntax**\n- Contains '{{' and '}}' pattern\n- Evaluated by the AFE 2.0 handlebars template engine\n- Can include logic, formatting, and computation\n- Access input record fields with {{record.fieldName}} notation; a grouped\n  (array) record binds as `rows` instead — {{rows.0.fieldName}} / {{#each rows}}\n- Examples: \"{{record.firstName}} {{record.lastName}}\", \"{{#if record.isActive}}Active{{else}}Inactive{{/if}}\"\n- Valid on scalars and on `object` rows, where the rendered output must\n  parse into an object. On an array row the template goes in a\n  `buildArrayHelper` entry instead: on an `objectarray` row's own\n  `extract` it is refused with 422 (\"buildArrayHelper\" or \"hardCodedValue\"\n  must be present for object array datatype.), and on a primitive-array\n  row's own `extract` it yields nothing (the field is left out) without an\n  error\n\n```json\n\"extract\": \"{{record.price}} {{record.currency}}\"  // Combines two fields\n```\n\n**3. Hard-Coded Value (literal string)**\n- Does NOT start with '$'\n- Does NOT contain handlebars '{{' syntax\n- System treats it as a literal string value\n- VERY COMMON for setting static/constant values\n- Examples: \"Active\", \"USD\", \"Completed\", \"true\"\n\n```json\n\"extract\": \"primary\"  // Sets field value to the literal string \"primary\"\n\"extract\": \"true\"     // Sets field value to the literal string \"true\"\n\"extract\": \"N/A\"      // Sets field value to the literal string \"N/A\"\n```\n\nThis third pattern is the simplest and most efficient way to set hard-coded values in your mappings.\nAI agents should use this pattern whenever a field needs a static value that doesn't come from\nthe input record or require computation.\n\n**Important implementation details**\n\n- JSON path patterns ALWAYS execute from the TOP-LEVEL root of the input record\n- The system maintains this context even in deeply nested mappings\n- For object mappings without child mappings, extract should return a complete object\n- An `object` row takes `extract` (to copy a whole object) or child `mappings`, not\n  both: a row with both fails with 422 (`\"extract\" should not be present if\n  \"mappings\" field is present`). A `buildArrayHelper` entry is different: there\n  `extract` and `mappings` work together.\n- The root is the record as it arrives: an object record roots at `$.field`; a\n  grouped record (an array of rows — grouped exports, file key columns, NetSuite\n  grouped saved searches) roots at the array, so its rows are addressed as\n  `$[0].field` (one row) or `$[*].field` (every row). A root that contradicts the\n  record's shape resolves to nothing without an error.\n\nFor most simple field-to-field mappings, prefer JSON path syntax for its clarity and performance.\nFor hard-coded values, simply use the literal string as the extract value.\n"},"extractDateFormat":{"type":"string","description":"Specifies the format pattern of the input date string to ensure proper parsing.\n\nUsed on string-typed mappings whose `extract` yields a date. Uses\nMoment.js-compatible formatting tokens to describe how the incoming date\nstring is structured.\n"},"extractDateTimezone":{"type":"string","description":"Specifies the timezone of the input date string using Olson/IANA timezone identifiers.\n\nUsed on string-typed mappings whose `extract` yields a date; tells the system\nhow to interpret timestamp values from the input system.\n"},"generateDateFormat":{"type":"string","description":"Specifies the output format pattern when generating a date string or converting\nfrom a Date type to String type.\n\nUses Moment.js-compatible formatting tokens to define the structure of the resulting\ndate string.\n"},"generateDateTimezone":{"type":"string","description":"Specifies the timezone to apply when generating or converting timestamp values\nusing Olson/IANA timezone identifiers.\n\nControls timezone conversion when producing date output.\n"},"default":{"type":["string","null"],"description":"Specifies a fallback value to use when extract returns empty/null or when conditional\nlogic fails and no other mapping supplies a value.\n\nExplicit JSON `null` is itself a valid fallback: the destination field is written as\nJSON null when the extract yields nothing (the Mapper UI's \"Use null as default value\"\naction). Omitting the key entirely means no fallback — the field is left out of the\noutput when the extract is missing or an empty string (the UI's \"Do nothing\" action),\nwhile a null source is still written as null unless `conditional.when` is\n`extract_not_empty`. Failing the record instead is `conditional.when:\nfail_record_when_empty`, which excludes a default.\n"},"lookupName":{"type":"string","description":"**Purpose**\nReferences a lookup table for transforming values during the mapping process.\n\n**Usage**\n\nThe lookupName refers to a named lookup defined in the lookups array of the same resource.\nOn an HTTP import that is `http.lookups`: a `lookups` array sent only at the top level\nis dropped on save. On an import, a name that matches no lookup fails every record\nwith \"Unable to find lookup in mapping configs - <name>\", and a key the lookup\ndoesn't hold follows the lookup's `allowFailures`: false fails the record; true, or\nthe field left out, lets the record succeed with the lookup's `default`, or without\nthe mapped field when there is none.\n\n```json\n{\n  \"generate\": \"countryName\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.countryCode\",\n  \"lookupName\": \"countryCodeToName\"\n}\n```\n\nDuring processing, the system:\n1. Extracts the value from the input record (e.g., \"US\")\n2. Finds the lookup table with the specified name\n3. Uses the extracted value as a key in the lookup\n4. Returns the corresponding value (e.g., \"United States\")\n\n**Benefits**\n\n- **Standardization**: Ensures consistent value translation across mappings\n- **Centralization**: Define translations once and reference them in multiple places\n- **Maintainability**: Update all mappings by changing the lookup definition\n- **Readability**: Makes mappings more descriptive and self-documenting\n\nThe specific lookup capabilities depend on the context where mappings are used.\n"},"hardCodedValue":{"type":"string","description":"Constant written to the destination instead of an extracted value. On a primitive\narray (`stringarray`, `numberarray`, `booleanarray`) the string is split on commas,\nverbatim and untrimmed, into elements of the array's type (`\"A,B\"` gives `[\"A\", \"B\"]`).\nOn an `object` it must be a JSON object string and on an `objectarray` a JSON array\nstring (an object string becomes one element); the save parses it and refuses non-JSON\nor the wrong shape with 422, and an array row with a constant needs no `buildArrayHelper`.\n"},"description":{"type":"string","description":"Optional free-text annotation that appears in the Mapper sidebar to provide context about\nthe mapping's purpose for collaboration and documentation.\n\nHas no functional impact on the mapping behavior.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the value produced by `extract`, before any\nconversion to `dataType`. Same enum as `dataType`. Set on leaf mappings\nonly — parent mappings (with child `mappings` or `buildArrayHelper`)\nhave no extracted value of their own; the children carry their own\n`sourceDataType`.\n\nFor date fields use `string` (JSON represents dates as strings); the\nparsing/formatting lives in `extractDateFormat` / `generateDateFormat` /\n`extractDateTimezone` / `generateDateTimezone`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"mappings":{"type":"array","description":"**Purpose**\nEnables recursive definition of nested object structures through child mapping objects.\n\n**Guidance**\nThis is the KEY FIELD that implements the recursive pattern at the core of this schema:\n\n**When to Use**\n- REQUIRED when dataType = \"object\" (unless you are copying an entire object from the input record)\n- REQUIRED in buildArrayHelper.mappings when defining complex object array elements\n- NEVER used with simple types (string, number, boolean)\n\n**Behavior**\n- Each mapping in this array becomes a property of the parent object\n- The full Mappings schema is repeated recursively at each level\n- Can be nested to any depth for complex hierarchical structures\n\n**Context Handling**\n- Each level of nesting changes the mapping CONTEXT for 'generate'\n- The extraction CONTEXT always remains the original input record\n- This means child mappings can pull data from anywhere in the input record\n\n**Common Patterns**\n\n**Nested Objects**\n```json\n{\n  \"generate\": \"customer\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\n      \"generate\": \"contact\",\n      \"dataType\": \"object\",\n      \"mappings\": [\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.customerEmail\"}\n      ]\n    }\n  ]\n}\n```\n\n**Multiple Fields in Object**\n```json\n{\n  \"generate\": \"address\",\n  \"dataType\": \"object\",\n  \"mappings\": [\n    {\"generate\": \"street\", \"dataType\": \"string\", \"extract\": \"$.address.line1\"},\n    {\"generate\": \"city\", \"dataType\": \"string\", \"extract\": \"$.address.city\"},\n    {\"generate\": \"country\", \"dataType\": \"string\", \"extract\": \"$.address.country\"}\n  ]\n}\n```\n\nIMPORTANT: When analyzing or generating mappings, ALWAYS check if parent.dataType = \"object\"\nor if you're inside buildArrayHelper.mappings for objectarray elements. These are the only\nvalid contexts for the mappings array.\n","items":{"$ref":"#/components/schemas/items"}},"buildArrayHelper":{"type":"array","description":"**Purpose**\nConfigures how to construct arrays in the output record, handling various array types and inputs.\n\n**Guidance**\nThis is the mechanism for building array data types:\n\n**When to Use**\n- REQUIRED when dataType ends with \"array\" (stringarray, objectarray, etc.), unless the\n  row sets `hardCodedValue`: an array row's own `extract` is never read (see `dataType`)\n- Each entry in this array contributes elements to the output array\n- Multiple entries allow combining data from different input arrays\n\n**Array Type Handling**\n\n**For Simple Arrays (stringarray, numberarray, booleanarray)**\n- Only the 'extract' field is used to pull values\n- JSON path with wildcards (e.g., $.items[*].name) returns multiple values;\n  on a grouped (array) record the record itself is the array, so the\n  path roots at it ($[*].name — one value per row)\n- Each result is converted to the appropriate primitive type\n```json\n{\n  \"generate\": \"productNames\",\n  \"dataType\": \"stringarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.products[*].name\"}\n  ]\n}\n```\n\n**For Object Arrays (objectarray) - three patterns**\n\n1. Extract Only (existing objects):\n```json\n{\n  \"generate\": \"contacts\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\"extract\": \"$.account.primaryContacts[*]\"},  // Pull primary contact objects\n    {\"extract\": \"$.account.secondaryContacts[*]\"},  // Pull secondary contact objects\n    {\"extract\": \"$.vendor.contactPersons[*]\"},  // Pull vendor contact objects\n    {\"extract\": \"$.subsidiaries[*].mainContact\"}  // Pull main contact from each subsidiary\n  ]\n}\n```\n\n2. Mappings Only (constructed object):\n```json\n{\n  \"generate\": \"contactInfo\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"mappings\": [  // Creates one object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"primary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.primaryEmail\"}\n      ]\n    },\n    {\n      \"mappings\": [  // Creates another object in the array\n        {\"generate\": \"type\", \"dataType\": \"string\", \"extract\": \"secondary\"},\n        {\"generate\": \"email\", \"dataType\": \"string\", \"extract\": \"$.secondaryEmail\"}\n      ]\n    }\n  ]\n}\n```\nEach constructed entry contributes exactly one element, in entry\norder; writing the entry with `\"extract\": \"$\"` is equivalent to\nomitting the extract (both anchor the element at the record root).\nA fixed number of static elements is built from that many sibling\nconstructed entries.\n\n3. Extract AND Mappings (transform input arrays):\n```json\n{\n  \"generate\": \"lineItems\",\n  \"dataType\": \"objectarray\",\n  \"buildArrayHelper\": [\n    {\n      \"extract\": \"$.order.items[*]\",  // For each item in the array\n      \"mappings\": [  // Transform to this structure using the composite object\n        {\"generate\": \"sku\", \"dataType\": \"string\", \"extract\": \"$.order.items.productId\"},  // Notice: items is singular\n        {\"generate\": \"quantity\", \"dataType\": \"number\", \"extract\": \"$.order.items.qty\"},   // Notice: items is singular\n        {\"generate\": \"orderNumber\", \"dataType\": \"string\", \"extract\": \"$.order.id\"},       // Access parent data\n        {\"generate\": \"customerName\", \"dataType\": \"string\", \"extract\": \"$.customerName\"}   // Access root data\n      ]\n    }\n  ]\n}\n```\n\n**Arrays of Arrays**\n- The mapper has no working type for them: `arrayarray` saves, but every record\n  fails with `Destination datatype is a required field.` Build an array of arrays\n  in a script hook instead.\n\n**Important details**\n\n- When both extract and mappings are provided, the system creates special composite objects\n  that maintain hierarchical context during processing\n- This enables accessing both the current array element AND its parent context\n- An ITERATING entry's extract MUST be a JSON path that iterates an array:\n  '$.items[*]' when the array is a field of an object record, or '$[*]'\n  (objectarray) / '$[*].field' (primitive arrays) when the record itself is a\n  grouped array of rows. A CONSTRUCTED entry (child mappings building one\n  element) instead omits extract or sets it to exactly \"$\" — never rewrite \"$\"\n  into an indexed or comma-joined form ('$[0]', '$[0],$[0]'): those render the\n  entire destination array as null without raising a validation error\n- Each array helper entry acts independently, potentially adding multiple elements\n\nThe buildArrayHelper is the most complex part of the mappings system - always analyze the\ndataType first to understand which pattern is appropriate.\n","items":{"type":"object","properties":{"extract":{"type":"string","description":"JSON path expression that identifies the input array or values to extract.\n\nFor objectarray with mappings, this defines which input objects to iterate through.\nThe JSON path must return either a single object or an array of objects.\n\nFor a CONSTRUCTED entry (child mappings building exactly one element from\nrecord-root fields and constants), omit this field or set it to exactly \"$\" —\nthe two spellings are equivalent. Indexed or comma-joined forms ('$[0]',\n'$[0],$[0]') are not valid at the entry level: the processor renders the\nentire destination array as null without raising a validation error.\n\nThe system creates special composite objects during processing to maintain\nhierarchical relationships, allowing easy access to both the current array item\nand its parent contexts.\n\nAn iterating entry keeps the `[*]`: `$.line_items` without it yields one empty\nelement per item, with no error. Child extracts drop the `[*]` but keep the\narray's path (`$.line_items.sku`); `$.line_items[*].sku` maps nothing, and an\nelement-relative `$.sku` reads the record root instead of the element.\n"},"name":{"type":"string","maxLength":200,"description":"Display label for this source object in the mapper, so the sources of\nan object array can be told apart instead of all showing as `$`.\nHas no effect at runtime; stored only when non-empty.\n"},"sourceDataType":{"type":"string","description":"Declares the JSON type of the input array being iterated, to ensure\nproper type handling during array construction. Same enum as `dataType`.\n","enum":["string","number","boolean","object","stringarray","numberarray","booleanarray","objectarray","arrayarray"]},"default":{"type":["string","null"],"description":"Specifies a fallback value when the extracted array element is empty or\nnot found in the input data.\n\nExplicit JSON `null` is itself a valid fallback (the element is written as\nJSON null); omitting the key means no fallback is applied.\n"},"conditional":{"type":"object","description":"Defines conditional rules for including each array element in the result.\n","properties":{"when":{"type":"string","description":"Condition judged on the entry's extract — skip the elements the\nextract does not return, or fail the whole record when the source\narray is missing or empty.\n","enum":["extract_not_empty","fail_record_when_empty"]}}},"mappings":{"type":"array","description":"Contains recursive mapping definitions for complex array element transformations.\n\n**Composite object mechanism**\n\nWhen both 'extract' and 'mappings' are used together, the system implements a sophisticated\n\"composite object\" approach that is crucial for AI agents to understand:\n\n1. The system starts with the complete input record\n\n2. For each array element matched by the extract path, it creates a modified version of\n   the input record where:\n   - Array paths in the extract JSON path are REPLACED with single objects\n   - Each array ([]) in the path is converted to a single object ({})\n   - This preserves the hierarchical relationship between nested arrays\n\n**Example**\n\nGiven an input record:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": [\n      {\n        \"id\": \"O-001\",\n        \"items\": [\n          {\"sku\": \"ABC\", \"qty\": 2},\n          {\"sku\": \"XYZ\", \"qty\": 1}\n        ]\n      },\n      {\n        \"id\": \"O-002\",\n        \"items\": [\n          {\"sku\": \"DEF\", \"qty\": 3}\n        ]\n      }\n    ]\n  }\n}\n```\n\nWith extract path: `$.customer.orders[*].items[*]`\n\nFor each item, the system creates a composite object like:\n```json\n{\n  \"customer\": {\n    \"name\": \"John Doe\",\n    \"orders\": {  // Note: Array replaced with single object\n      \"id\": \"O-001\",\n      \"items\": {  // Note: Array replaced with single object\n        \"sku\": \"ABC\",\n        \"qty\": 2\n      }\n    }\n  }\n}\n```\n\nThen in your mappings, you can access:\n- The current item: `$.customer.orders.items.sku`\n- The parent order: `$.customer.orders.id`\n- Top-level data: `$.customer.name`\n\nThis approach allows for precise mapping from deeply nested structures while maintaining\naccess to all contextual parent data, without requiring complex array index management.\n\n**Implementation guidance**\n\nWhen implementing the composite object mechanism:\n\n1. Analyze the extract path to identify all array patterns (`[*]` or `[number]`)\n2. For each array in the path, understand that it will be replaced with a single object\n3. In the mappings, use paths that reference these arrays as if they were objects\n4. Remember that every mapping still has access to the full input record context\n5. This mechanism is especially powerful when mapping hierarchical data like:\n   - Order → Line Items → Taxes/Discounts\n   - Customer → Addresses → Address Lines\n   - Invoice → Line Items → Serial Numbers\n\nThe extract path effectively tells the system \"iterate through these arrays\",\nwhile the composite object mechanism ensures you can still access both the\ncurrent array item AND its parent context during mapping.\n","items":{"$ref":"#/components/schemas/items"}}},"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"required":["default"]}}}},"status":{"type":"string","description":"**Purpose**\nControls whether the mapping is applied. Send it on every mapping entry.\n\n**Guidance**\nOnly `Active` rows run. `Draft` and `Inactive` rows keep their\nconfiguration, are saved with minimal validation, and are skipped at\nruntime and in preview; a non-Active parent skips its whole subtree\nwhatever its children say. Imports (on save and in the mapper processor)\nand API response mappings reject a row missing this field (validation\nerror: \"Mapping object must have status field present.\"); a Tool's\noutput mapping saves and runs one, which is why `required` doesn't\nlist it.\n","enum":["Active","Draft","Inactive"]},"conditional":{"type":"object","description":"**Purpose**\nDefines conditional processing rules for the entire mapping.\n\n**Guidance**\nThese conditions determine whether the mapping is applied based on record\nstate or field content:\n\n**When to Use**\n- When a mapping should only be applied in specific circumstances\n- To implement conditional logic without using complex handlebars expressions\n- For creating mappings that only run during create or update operations\n\n**Available Conditions**\n\n- `record_created`: Apply only when creating a new record\n  Useful for setting initial values that should not be overwritten during updates\n\n- `record_updated`: Apply only when updating an existing record\n  Useful for transformation logic that should only run during updates\n\n- `extract_not_empty`: Apply only when the extract field returns a value\n  Useful for conditional mapping based on input data availability\n\n- `fail_record_when_empty`: Fail the record when the extract field returns\n  no value (the UI's \"Cause record to fail\" action when the source field\n  has no value)\n\n**Example**\n```json\n{\n  \"generate\": \"statusMessage\",\n  \"dataType\": \"string\",\n  \"status\": \"Active\",\n  \"extract\": \"$.status.message\",\n  \"conditional\": {\n    \"when\": \"extract_not_empty\"  // Only map when status.message exists\n  }\n}\n```\n","properties":{"when":{"type":"string","description":"Condition that gates this mapping — either an operation gate on a\ncomposite (create-or-update) import or the action to take when the\nextract returns no value. Empty means null, undefined or an empty\nstring; `0`, `false` and whitespace are values.\n","enum":["record_created","record_updated","extract_not_empty","fail_record_when_empty"]}}}},"required":["dataType"],"if":{"properties":{"conditional":{"properties":{"when":{"const":"fail_record_when_empty"}},"required":["when"]}},"required":["conditional"]},"then":{"not":{"anyOf":[{"required":["default"]},{"required":["lookupName"]},{"required":["hardCodedValue"]}]}}},"Filter":{"type":"object","description":"Configuration for selectively processing records based on specified criteria. This object enables\nprecise control over which items are included or excluded from processing operations.\n\n**Filter behavior**\n\nWhen configured, the filter is applied before processing begins:\n- Items that match the filter criteria are processed\n- Items that don't match are completely skipped\n- No partial processing is performed\n\nSend the whole object. A rules array sent alone as the `filter` value is not\nstored, and a full update of an export that carries one removes the filter the\nexport had, with no error.\n\n**Implementation approaches**\n\nThere are two distinct filtering mechanisms available:\n\n**Rule-Based Filtering (`type: \"expression\"`)**\n- **Best For**: Common filtering patterns based on standard attributes\n- **Capabilities**: Filter by names, values, dates, numerical ranges, text patterns\n- **Advantages**: Declarative, no coding required, consistent performance\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear, static criteria for selection\n\n**Script-Based Filtering (`type: \"script\"`)**\n- **Best For**: Complex logic, dynamic criteria, or business rules\n- **Capabilities**: Full programmatic control, access to complete metadata\n- **Advantages**: Maximum flexibility, can implement any filtering logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Simple rules aren't sufficient or logic needs to be dynamic\n","properties":{"type":{"type":"string","description":"Determines which filtering mechanism to use. This choice affects which properties\nmust be configured and how filtering logic is implemented.\n\n**Available types**\n\n**Rule-Based Filtering (`\"expression\"`)**\n- **Required Config**: The `expression` object with rule definitions\n- **Behavior**: Evaluates declarative rules against item attributes\n- **Best For**: Common patterns like name matching, date ranges, value limits\n- **Advantages**: Simpler to configure, no custom code required\n\n**Script-Based Filtering (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to determine which items to process\n- **Best For**: Complex conditions, business logic, dynamic criteria\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard filtering needs (name, size, date), use `\"expression\"`\n2. For complex logic or conditions not covered by expressions, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based filtering. This object enables filtering\nitems based on common attributes without requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define filtering rules that\ncan match against item attributes like name, type, value, date, and other properties.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Rules can be combined with AND/OR logic\n- Each rule can check a specific attribute\n- Multiple conditions can be applied (ranges, pattern matching, exact matches)\n\n**Common filter patterns**\n\n1. **Pattern matching**: Regular expressions with `matches`\n2. **Value range filtering**: Numbers between min and max values\n3. **Date range filtering**: Items created/modified within specific time ranges\n4. **Status checking**: Items with specific status values or properties\n\nFor AI agents: Rule-based filtering should be your first choice when the filtering criteria\ncan be expressed in terms of standard attributes. Only use script-based filtering when\nmore complex logic is required.\n","properties":{"version":{"type":"string","description":"Version identifier for the expression format. Currently only version \"1\" is supported.\n\nThis field ensures future compatibility if the expression format evolves. Always set to \"1\"\nfor current implementations.\n","enum":["1"]},"rules":{"type":"array","description":"Expression array defining filter conditions using prefix notation. The first element is the operator,\nfollowed by its operands which may themselves be nested expression arrays.\n\nThe rule expression follows this pattern:\n- First element: Operator name (string)\n- Remaining elements: Operands for that operator (values or nested expressions)\n\n**Expression structure**\n\nFilter expressions use a prefix notation where operators appear before their operands:\n```\n[operator, operand1, operand2, ...]\n```\n\n**Comparison Operators**\n- `\"equals\"`: Exact match (equals)\n- `\"notequals\"`: Not equal to value (not equals)\n- `\"greaterthan\"`: Value is greater than specified value (is greater than)\n- `\"greaterthanequals\"`: Value is greater than or equal to specified value (is greater than or equals)\n- `\"lessthan\"`: Value is less than specified value (is less than)\n- `\"lessthanequals\"`: Value is less than or equal to specified value (is less than or equals)\n- `\"startswith\"`: String starts with specified prefix (starts with)\n- `\"notstartswith\"`: String does not start with specified prefix (does not start with)\n- `\"endswith\"`: String ends with specified suffix (ends with)\n- `\"notendswith\"`: String does not end with specified suffix (does not end with)\n- `\"contains\"`: String contains specified substring (contains)\n- `\"doesnotcontain\"`: String does not contain specified substring (does not contain)\n- `\"empty\"`: Field is missing or empty (is empty)\n- `\"notempty\"`: Field contains a value (is not empty)\n- `\"matches\"`: Matches a regular expression, ignoring case (matches)\n\nOperator names are case-insensitive: `NOTEMPTY` and `endsWith` work and are stored\nas sent. `isempty`, `isnotempty`, `anyof`, `notcontains`, `doesnotstartwith`,\n`doesnotendwith`, `notmatches`, `doesnotmatch` and `notequal` are not operators:\nthey are refused with 422 \"Invalid operation: <name>\", with the name in lowercase,\nboth when the filter is saved and by `POST /v1/processors/filter`. To negate a\ncondition, wrap it in `not`.\n\nThe string comparisons (`equals`, `contains`, `startswith`, `endswith` and their\nnegations) are case-sensitive; wrap the field in `lowercase` to compare without case.\n`matches` is not: every pattern is compiled with the flags `gi`, so `^paid$` also\nmatches `PAID` and `[a-z]` matches capital letters. An inline flag such as `(?i)`\nmakes the pattern invalid. The filter still saves, but in a run every record fails\nwith `FILTER_EVALUATION_FAILED` (\"Invalid regular expression: /(?i)…/gi: Invalid\ngroup\"), and `POST /v1/processors/filter` refuses the call with 422 (live-verified\n2026-10-22).\n\n**Logical Operators**\n- `\"and\"`: All conditions must be true\n- `\"or\"`: At least one condition must be true\n- `\"not\"`: Negates the condition\n\n**Field Access and Type Conversion**\n- `\"extract\"`: Access a field from the item by name\n- `\"settings\"`: Access a custom setting from the flow, flow step, or integration configuration\n- `\"boolean\"`: Convert value to Boolean type\n- `\"epochtime\"`: Convert value to Epoch Time (Unix timestamp)\n- `\"number\"`: Convert value to Number type\n- `\"string\"`: Convert value to String type\n\n**Field Access Details**\n\n**Using `extract` to access record fields:**\n- Retrieves values from the current record being processed\n- Can access nested properties using dot notation (e.g., `\"customer.email\"`)\n- Paths are relative to the record itself: `[\"extract\", \"record.status\"]`\n  matches nothing\n- In a record filter, must be wrapped in a type conversion (`string`, `number`,\n  `boolean` or `epochtime`): a bare `[\"extract\", ...]` operand is refused with 422\n  \"Field operation extract must be called by a Type Conversion Operation but was called\n  by <operator>\", both when an import's filter is saved and by\n  `POST /v1/processors/filter`\n\n**Using `settings` to access configuration values:**\n- Retrieves values from the integration's configuration settings\n- Supports different scopes with prefix notation:\n  - `flow.settingName`: Access flow-level settings\n  - `flowGrouping.settingName`: Access the flow group's settings\n  - `export.settingName`: Access the export's (or lookup's) own settings\n  - `import.settingName`: Access the import's own settings\n  - `integration.settingName`: Access integration-level settings\n  - `connection.settingName`, `iClient.settingName`: Access the step's\n    connection and iClient settings\n- A scope whose resource has no settings can be absent; a rule that reads\n  it matches nothing\n- Must be wrapped in a type conversion like `extract`: a bare\n  `[\"settings\", ...]` operand is refused with 422 \"Field operation settings\n  must be called by a Type Conversion Operation\"\n- Useful for dynamic filtering based on configuration\n\n**Field Transformations**\n- `\"lowercase\"`: Convert string to lowercase\n- `\"uppercase\"`: Convert string to uppercase\n- `\"ceiling\"`: Round number up to the nearest integer\n- `\"floor\"`: Round number down to the nearest integer\n- `\"abs\"`: Get absolute value of a number\n\n**Arithmetic** (number operands)\n- `\"add\"`, `\"subtract\"`, `\"multiply\"`, `\"divide\"`, `\"modulo\"`: for example\n  `[\"subtract\", [\"number\", [\"extract\", \"total\"]], [\"number\", [\"extract\", \"discount\"]]]`.\n  `mod` is not an operator (422 \"Invalid operation: mod.\").\n\n**Dates**\n- `epochtime` reads ISO 8601 strings and epoch-millisecond numbers. An empty\n  string, a string that isn't a date, or epoch milliseconds sent as a string\n  fail it with \"Invalid date. Cannot convert given string or number to date\n  time.\", followed by `; record=` and the record as JSON: in a run that record fails (`FILTER_EVALUATION_FAILED`, source\n  `output_filter` on an export), and `POST /v1/processors/filter` and an\n  export preview refuse the whole call with 422. `and` and `or` evaluate\n  every operand, so a `notempty` test beside it doesn't prevent the failure.\n  A missing or null field doesn't match.\n- `datetime`, `date` and `timestamp` are not type conversions: 422\n  \"Invalid operation: <name>.\"\n- `number` reads a missing or empty field as 0.\n\n**Handlebars operands**\n- An operand can also be a string holding exactly one Handlebars expression, such as\n  `\"{{record.status}}\"` or `\"{{lowercase record.code}}\"`, on either side of the\n  operator. It renders against the record (`record.<field>`; an unprefixed\n  `{{status}}` renders empty) before the comparison.\n- The rendered value is a string: wrap it in `[\"number\", ...]` to compare it with a\n  number, or the rule is refused with 422.\n- `epochtime` does not render Handlebars: `[\"epochtime\", \"{{timestamp}}\"]` is refused\n  with 422 \"invalid datetime format\".\n- A string with text around the expression, or with more than one expression, is\n  compared as a literal.\n- `[\"number\", \"{{timestamp \\\"x\\\"}}\"]` is the current time in epoch milliseconds,\n  so `[\"greaterthanequals\", [\"epochtime\", [\"extract\", \"created\"]], [\"subtract\",\n  [\"number\", \"{{timestamp \\\"x\\\"}}\"], 604800000]]` keeps records created in the\n  last 7 days.\n- An unknown helper fails the rule with 422 \"Failed to evaluate handlebars\n  expression\".\n\nChoose the type conversion that matches the comparison — `number` for numeric\ncomparisons, `epochtime` for dates, `string` for text. For example:\n\n```json\n[\n  \"equals\",\n  [\n    \"number\",  // Convert to number before comparison\n    [\n      \"extract\",\n      \"quantity\"\n    ]\n  ],\n  100\n]\n```\n\nExample with datetime conversion:\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",  // Convert to Unix timestamp before comparison\n    [\n      \"extract\",\n      \"createdDate\"\n    ]\n  ],\n  1609459200000  // January 1, 2021 as Unix timestamp in milliseconds\n]\n```\n\nExample with transformations:\n```json\n[\n  \"and\",\n  [\n    \"matches\",\n    [\n      \"lowercase\",  // Convert to lowercase before matching\n      [\n        \"string\",\n        [\n          \"extract\",\n          \"categories\"\n        ]\n      ]\n    ],\n    \"netsuite\"\n  ],\n  [\n    \"notequals\",\n    [\n      \"string\",\n      [\n        \"extract\",\n        \"recurrence.pattern.type\"\n      ]\n    ],\n    \"\"\n  ]\n]\n```\n\nExample comparing a record field with a flow setting:\n```json\n[\n  \"equals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"trantype\"\n    ]\n  ],\n  [\n    \"string\",\n    [\n      \"settings\",\n      \"flow.trantype\"\n    ]\n  ]\n]\n```\n\n**Examples**\n\nExample 1: Status field is not equal to \"cancelled\"\n```json\n[\n  \"notequals\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"status\"\n    ]\n  ],\n  \"cancelled\"\n]\n```\n\nExample 2: Filename starts with \"HC\"\n```json\n[\n  \"startswith\",\n  [\n    \"string\",\n    [\n      \"extract\",\n      \"filename\"\n    ]\n  ],\n  \"HC\"\n]\n```\n\nExample 3: Amount is greater than 100\n```json\n[\n  \"greaterthan\",\n  [\n    \"number\",\n    [\n      \"extract\",\n      \"amount\"\n    ]\n  ],\n  100\n]\n```\n\nExample 4: Order date is after January 1, 2023 (UTC). Compare dates as `epochtime`\nagainst epoch milliseconds: a `string` comparison of ISO timestamps orders values with\ndifferent UTC offsets wrongly.\n```json\n[\n  \"greaterthan\",\n  [\n    \"epochtime\",\n    [\n      \"extract\",\n      \"orderDate\"\n    ]\n  ],\n  1672531200000\n]\n```\n","items":{"oneOf":[{"title":"String","type":"string"},{"title":"Number","type":"number"},{"title":"Boolean","type":"boolean"},{"title":"Object","type":"object"},{"title":"Array","type":"array"},{"title":"Null","type":"null"}]}}}},"script":{"type":"object","description":"Configuration for programmable script-based filtering. This object enables complex, custom\nfiltering logic beyond what expression-based filtering can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `filter.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to determine which items\nshould be processed.\n\n**Implementation approach**\n\nScript-based filtering works by:\n1. Executing the specified function from the referenced script\n2. Passing item data to the function\n3. Using the function's return value (true/false) to determine inclusion\n\n**Common use cases**\n\nScript filtering is ideal for:\n- Complex business logic that can't be expressed as simple rules\n- Dynamic filtering criteria that change based on external factors\n- Content-based filtering that requires deep inspection\n- Advanced pattern matching beyond simple wildcards\n- Multi-stage filtering with intermediate logic\n\nFor AI agents: Only use script-based filtering when expression-based filtering is insufficient.\nScript filtering requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to the Script resource that contains the filtering logic. This must be a valid\nObjectId of a Script resource in this account: any other id fails the save with 422\n(\"Only a script owned by the user or a script shared by the Integration App can be used\nat filter.script._scriptId.\").\n\nThe referenced script must contain the function specified in the `function` field\nand must be written to handle filtering specifically. The script receives\nitem data as its input and must return a boolean value indicating whether\nto process the item (true) or skip it (false).\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n"},"function":{"type":"string","description":"Name of the function within the script to execute for filtering decisions. This function\nmust exist in the script referenced by _scriptId.\n\n**Function requirements**\n\nThe specified function must:\n- Accept item data as its first parameter\n- Return a boolean value (true to process the item, false to skip it)\n- Handle errors gracefully\n- Execute efficiently (as it may run for many items)\n\n**Function signature**\n\n```javascript\nfunction filterItems(itemData) {\n  // itemData contains properties of the item being evaluated\n  // Custom logic here\n  return true; // or false to skip the item\n}\n```\n\nFor AI agents: Ensure the function name exactly matches a function defined in the\nreferenced script, as mismatches will cause the filter to fail.\n"}}}}},"Transform":{"type":"object","description":"Configuration for transforming data during processing operations. This object enables\nreshaping of records.\n\n**Transformation capabilities**\n\nCeligo's transformation engine offers powerful features for data manipulation:\n- Precise field mapping with JSONPath expressions\n- Support for any level of nested arrays\n- Formula-based field value generation\n- Dynamic references to flow and integration settings\n\n**Implementation approaches**\n\nThere are two distinct transformation mechanisms available:\n\n**Rule-Based Transformation (`type: \"expression\"`)**\n- **Best For**: Most transformation scenarios from simple to complex\n- **Capabilities**: Field mapping, formula calculations, lookups, nested data handling\n- **Advantages**: Visual configuration, no coding required, intuitive interface\n- **Configuration**: Define rules in the `expression` object\n- **Use When**: You have clear mapping requirements or need to reshape data structure\n\n**Script-Based Transformation (`type: \"script\"`)**\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Capabilities**: Full programmatic control, custom processing, complex business rules\n- **Advantages**: Maximum flexibility, can implement any transformation logic\n- **Configuration**: Reference a script in the `script` object\n- **Use When**: Visual transformation tools aren't sufficient for your use case\n","properties":{"type":{"type":"string","description":"Determines which transformation mechanism to use. This choice affects which properties\nmust be configured and how transformation logic is implemented.\n\n**Available types**\n\n**Rule-Based Transformation (`\"expression\"`)**\n- **Required Config**: The `expression` object with mapping definitions\n- **Behavior**: Applies declarative rules to reshape data\n- **Best For**: Most transformation scenarios from simple to complex\n- **Advantages**: Visual configuration, no coding required\n\n**Script-Based Transformation (`\"script\"`)**\n- **Required Config**: The `script` object with _scriptId and function\n- **Behavior**: Executes custom JavaScript to transform data\n- **Best For**: Extremely complex logic or proprietary algorithms\n- **Advantages**: Maximum flexibility, can implement any logic\n\n**Implementation guidance**\n\n1. For standard data transformations, use `\"expression\"`\n2. For complex logic or specialized processing, use `\"script\"`\n3. When selecting a type, you must configure the corresponding object:\n    - `type: \"expression\"` requires the `expression` object\n    - `type: \"script\"` requires the `script` object\n","enum":["expression","script"]},"expression":{"type":"object","description":"Configuration for declarative rule-based transformations. This object enables reshaping data\nwithout requiring custom code.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"expression\" and should not be\nconfigured otherwise. It provides a standardized way to define transformation rules that\ncan map, modify, and generate data elements.\n\n**Implementation guidance**\n\nThe expression system uses a rule-based approach where:\n- Field mappings define how input data is transformed to target fields\n- Formulas can be used to calculate or generate new values\n- Lookups can enrich data by fetching related information\n- Mode determines how records are processed (create new or modify existing)\n","properties":{"version":{"type":"string","description":"Version of the expression format. Determines which rules\nproperty contains the transformation logic.\n","enum":["1","2"]},"rules":{"type":"array","description":"Transformation rules for version 1 expressions. An array of\nrule groups; each group is an array of field-mapping objects.\nMost transforms have a single group. Present when `version`\nis `\"1\"`. The output record contains ONLY the generated\nfields — every unmapped field is dropped (v1 has no\nequivalent of Transform 2.0's `modify` mode), and the\nrecord's trace key does not survive the rebuild.\n","items":{"type":"array","items":{"type":"object","properties":{"extract":{"type":"string","description":"Source field path to read from. Supports multiple\nsyntaxes: bare field names (`id`), dot notation\n(`fulfillment.shipment_id`), slash-prefixed paths\nfor XML (`/FeedProcessingStatus`), wildcards (`*.id`,\n`*.[Internal ID]`), and array indexing (`SDF[0]`).\n"},"generate":{"type":"string","description":"Target field name to write to. Typically a bare name\n(`id`) or dot path (`SDF.Filter.ID`).\n"},"key":{"type":"string","description":"Auto-generated identifier for this rule, used by the\nUI to track individual rules for editing and reordering.\n"}},"required":["extract","generate"]}}},"rulesTwoDotZero":{"type":"object","description":"Configuration for version 2 transformation rules. This object contains the core logic\nfor how data is mapped, enriched, and transformed.\n\n**Capabilities**\n\nTransformation 2.0 provides:\n- Precise field mapping with JSONPath expressions\n- Support for deeply nested data structures\n- Formula-based field generation\n- Dynamic lookups for data enrichment\n- Multiple operating modes to fit different scenarios\n","properties":{"mode":{"type":"string","description":"Transformation mode that determines how records are handled during processing.\n\n**Available modes**\n\n**Create Mode (`\"create\"`)**\n- **Behavior**: Builds entirely new output records from inputs\n- **Use When**: Output structure differs significantly from input\n- **Advantage**: Clean slate approach, no field inheritance\n\n**Modify Mode (`\"modify\"`)**\n- **Behavior**: Makes targeted edits to existing records\n- **Use When**: Output structure should remain similar to input\n- **Advantage**: Preserves unmapped fields from the original record\n","enum":["create","modify"]},"mappings":{"$ref":"#/components/schemas/Mappings"},"lookups":{"allOf":[{"description":"Shared lookup tables used across all mappings defined in the transformation rules.\n\n**Purpose**\n\nLookups provide centralized value translation that can be referenced from any mapping\nin your transformation configuration. They enable consistent translation of codes, IDs,\nand values between systems without duplicating translation logic.\n\n**Usage in transformations**\n\nLookups are particularly valuable in transformations for:\n\n- **Data Normalization**: Standardizing values from diverse source systems\n- **Code Translation**: Converting between different coding systems (e.g., status codes)\n- **Field Enrichment**: Adding descriptive values based on ID or code lookups\n- **Cross-Reference Resolution**: Mapping identifiers between integrated systems\n\n**Implementation**\n\nLookups are defined once in this array and referenced by name in mappings:\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"statusMapping\",\n    \"map\": {\n      \"A\": \"Active\",\n      \"I\": \"Inactive\",\n      \"P\": \"Pending\"\n    },\n    \"default\": \"Unknown Status\"\n  }\n]\n```\n\nThen referenced in mappings using the lookupName property:\n\n```json\n{\n  \"generate\": \"status\",\n  \"dataType\": \"string\",\n  \"extract\": \"$.statusCode\",\n  \"lookupName\": \"statusMapping\"\n}\n```\n\nThe system automatically applies the lookup during transformation processing.\n\nFor complete details on lookup properties and behavior, see the Lookups schema.\n"},{"$ref":"#/components/schemas/Lookups"}]},"inputContext":{"type":"string","enum":["record","envelope"],"description":"Controls the JSON shape the transformTwoDotZero processor\nevaluates `mappings[].extract` JSONPath values against at\nflow runtime. Applies only to Transform 2.0 (v2,\n`rulesTwoDotZero`); v1 transforms (the `rules` array on\n`transform.expression.rules`) and script-mode transforms\nignore this field.\n"}}}}},"script":{"type":"object","description":"Configuration for programmable script-based transformations. This object enables complex, custom\ntransformation logic beyond what expression-based transformations can provide.\n\n**Usage context**\n\nThis object is REQUIRED when `transform.type` is set to \"script\" and should not be configured\notherwise. It provides a way to execute custom JavaScript code to transform data according to\nspecialized business rules or complex algorithms.\n\n**Implementation approach**\n\nScript-based transformation works by:\n1. Executing the specified function from the referenced script\n2. Passing input data to the function\n3. Using the function's return value as the transformed output\n\n**Common use cases**\n\nScript transformation is ideal for:\n- Complex business logic that can't be expressed through mappings\n- Algorithmic transformations requiring computation\n- Dynamic transformations based on external factors\n- Legacy system data format compatibility\n- Multi-stage processing with intermediate steps\n\nOnly use script-based transformation when expression-based transformation is insufficient.\nScript transformation requires maintaining custom code, which adds complexity to the integration.\n","properties":{"_scriptId":{"type":"string","description":"Reference to a predefined script resource containing the transformation logic.\n\nThe referenced script should contain the function specified in the\n'function' property. It must be a script in this account: any other id fails the save\nwith 422 (\"Only a script owned by the user or a script shared by the Integration App can\nbe used at transform.script._scriptId.\").\n","format":"objectid"},"function":{"type":"string","description":"Name of the function within the script to execute for transformation. This function\nmust exist in the script referenced by _scriptId.\n"}}}}},"Lookups":{"type":"array","description":"Configuration for value-to-value transformations using lookup tables.\n\n**Purpose**\n\nLookups provide a way to translate values from one system to another. They transform\ninput values into output values using either static mapping tables or\ndynamic lookup caches.\n\n**Lookup mechanisms**\n\nThere are two distinct lookup mechanisms available:\n\n1. **Static Lookups**: Define a simple key-value map object and store it as part of your resource\n   - Best for: Small, fixed sets of values that rarely change\n   - Implementation: Configure the `map` object with input-to-output value mappings\n   - Example: Country codes, status values, simple translations\n\n2. **Dynamic Lookups**: Reference an existing 'Lookup Cache' resource in your Celigo account\n   - Best for: Large datasets, frequently changing values, or complex reference data\n   - Implementation: Configure `_lookupCacheId` to reference cached data maintained independently\n   - Example: Product catalogs, customer databases, pricing information\n\n**Property usage**\n\nThere are two mutually exclusive ways to configure lookups, depending on which mechanism you choose:\n\n1. **For Static Mappings**: Configure the `map` property with a direct key-value object\n   ```json\n   \"map\": {\"US\": \"United States\", \"CA\": \"Canada\"}\n   ```\n\n2. **For Dynamic Lookups**: Configure the following properties:\n   - `_lookupCacheId`: Reference to the lookup cache resource\n   - `extract`: JSON path to extract specific value from the returned lookup object\n\n**When to use**\n\nLookups are ideal for:\n\n1. **Value Translation**: Mapping codes or IDs to human-readable values\n\n2. **Data Enrichment**: Adding related information to records during processing\n\n3. **Normalization**: Ensuring consistent formatting of values across systems\n\n**Implementation details**\n\nLookups can be referenced in:\n\n1. **Field Mappings**: Direct use in field transformation configurations\n\n2. **Handlebars Templates**: Use within templates with the syntax:\n   ```\n   {{lookup 'lookupName' record.fieldName}}\n   ```\n\n**Example usage**\n\n```json\n\"lookups\": [\n  {\n    \"name\": \"countryCodeToName\",\n    \"map\": {\n      \"US\": \"United States\",\n      \"CA\": \"Canada\",\n      \"UK\": \"United Kingdom\"\n    },\n    \"default\": \"Unknown Country\",\n    \"allowFailures\": true\n  },\n  {\n    \"name\": \"productDetails\",\n    \"_lookupCacheId\": \"60a2c4e6f321d800129a1a3c\",\n    \"extract\": \"$.details.price\",\n    \"allowFailures\": false\n  }\n]\n```\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for the lookup table within this configuration.\n\nThis name must be unique within the scope where the lookup is defined and is used to reference\nthe lookup in handlebars templates with the syntax {{lookup 'name' value}}.\n\nChoose descriptive names that indicate the transformation purpose, such as:\n- \"countryCodeToName\" for country code to full name conversion\n- \"statusMapping\" for status code translations\n- \"departmentCodes\" for department code to name mapping\n"},"map":{"type":["object","null"],"description":"The lookup mapping table as key-value pairs. The platform stores `null`\nhere on dynamic lookups, which resolve values at runtime instead of\nfrom a static table.\n\nThis object contains the input values as keys and their corresponding\noutput values. When a input value matches a key in this object,\nit will be replaced with the corresponding value.\n\nThe map should be kept to a reasonable size (typically under 100 entries)\nfor optimal performance. For larger mapping requirements, consider using\ndynamic lookups instead.\n\nMaps can include:\n- Simple code to name conversions: {\"US\": \"United States\"}\n- Status transformations: {\"A\": \"Active\", \"I\": \"Inactive\"}\n- ID to name mappings: {\"100\": \"Marketing\", \"200\": \"Sales\"}\n\nValues can be strings, numbers, or booleans, but all are stored as strings\nin the configuration.\n"},"_lookupCacheId":{"type":"string","description":"Reference to a LookupCache resource that contains the reference data for the lookup.\n\n**Purpose**\n\nThis field connects the lookup to an external data source that has been cached in the system.\nUnlike static lookups that use the `map` property, dynamic lookups can reference large datasets\nor frequently changing information without requiring constant updates to the integration.\n\n**Implementation details**\n\nThe LookupCache resource referenced by this ID contains:\n- The data records to be used as a reference source\n- Configuration for how the data should be indexed and accessed\n- Caching parameters to balance performance with data freshness\n\n**Usage patterns**\n\nCommonly used to reference:\n- Product catalogs or SKU databases\n- Customer or account information\n- Pricing tables or discount rules\n- Complex business logic lookup tables\n\nFormat: 24-character hexadecimal string (MongoDB ObjectId)\n","format":"objectid"},"extract":{"type":"string","description":"JSON path expression that extracts a specific value from the cached lookup object.\n\n**Purpose**\n\nWhen using dynamic lookups with a LookupCache, this JSON path identifies which field to extract\nfrom the cached object after it has been retrieved using the lookup key.\n\n**Implementation details**\n\n- Must use JSON path syntax (similar to mapping extract fields)\n- Operates on the cached object returned by the lookup operation\n- Examples:\n  - \"$.name\" - Extract the name field from the top level\n  - \"$.details.price\" - Extract a nested price field\n  - \"$.attributes[0].value\" - Extract a value from the first element of an array\n\n**Usage scenario**\n\nWhen a lookup cache contains complex objects:\n```json\n// Cache entry for key \"PROD-123\":\n{\n  \"id\": \"PROD-123\",\n  \"name\": \"Premium Widget\",\n  \"details\": {\n    \"price\": 99.99,\n    \"currency\": \"USD\",\n    \"inStock\": true\n  }\n}\n```\n\nSetting extract to \"$.details.price\" would return 99.99 as the lookup result.\n\nIf no extract is provided, the entire cached object is returned as the lookup result.\n"},"default":{"type":["string","null"],"description":"Default value to use when the source value is not found in the lookup map.\nThe platform stores `null` here when no default is configured.\n\nThis value is used as a fallback when:\n1. The source value doesn't match any key in the map\n2. allowFailures is true or omitted\n\nSetting an appropriate default helps prevent flow failures due to unexpected\nvalues and provides predictable behavior for edge cases.\n\nCommon default patterns include:\n- Descriptive unknowns: \"Unknown Country\", \"Unspecified Status\"\n- Original value indicators: \"{Original Value}\", \"No mapping found\"\n- Neutral values: \"Other\", \"N/A\", \"Miscellaneous\"\n\nA save refuses a default together with an explicit `allowFailures: false`\n(422 `Lookups with default set can not have allowFailures explicitly set to\nfalse.`): a lookup either fails a miss (`allowFailures: false`, no default) or\nfalls back to its default.\n"},"allowFailures":{"type":["boolean","null"],"description":"When true, missing lookup values will use the default value rather than causing an error.\n\n**Behavior control**\n\nThis field determines how the system handles source values that don't exist in the map:\n\n- true: Use the default value for missing mappings and continue processing\n- false: Treat missing mappings as errors, failing the record (`lookup_error` for a\n  static map, `lookupcache_evaluation_failed` for a lookup cache)\n- omitted or null: the record doesn't fail either. On an import a static lookup writes\n  its default if it has one, and a static or cache lookup without one leaves the\n  mapped field out of the output, with no error\n\n**Recommendation**\n\nSet this to true when:\n- New source values might appear over time\n- Data quality issues could introduce unexpected values\n- Processing should continue even with imperfect mapping\n\nSet this to false when:\n- Complete data accuracy is critical\n- All possible source values are known and controlled\n- Missing mappings indicate serious data problems that should be addressed\n\nA default raises no error: records that miss go out with the default value and\nnothing reports them. Use true only when that fallback is acceptable at the\ndestination, and set false explicitly when a missing value must stop the record.\n"}}}},"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: one in the script named by `_scriptId`, or one the code deployed on\nthe stack named by `_stackId` exposes."},"_scriptId":{"type":"string","format":"objectId","description":"Script containing the hook function named in `function`. Send this or `_stackId`, not both.\nMust be a script in this account: any other id fails the save with 422, naming the hook,\nsuch as \"Only a script owned by the user or a script shared by the Integration App can\nbe used at hooks.preMap._scriptId.\" on an import's `preMap`."},"_stackId":{"type":"string","format":"objectId","description":"Stack that hosts the hook logic, used instead of a script for stack-based deployments.\nThe API doesn't enforce the either/or: a save carrying both `_scriptId` and `_stackId`\nanswers 200 and stores both, and the script and the stack then each list the step as a\ndependent."},"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."}}},"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"},"ChatStreamEvent":{"type":"object","description":"One server-sent event of a chat turn. The stream is `text/event-stream`:\neach event is an `event:` line naming the event and one or more `data:`\nlines carrying its payload, separated by a blank line. `data` is JSON for\nevery event except `responsechunk`, whose `data:` lines are raw text (one\nline per line of the delta). A successful turn is `init`, `thinking`\n(started), any number of `progress` events, `thinking` (completed), the\n`responsechunk` deltas, `usage`, `complete`; a Celigo Tool's inner-step\n`progress` events can still arrive after `thinking` (completed).","required":["event"],"properties":{"event":{"type":"string","enum":["init","thinking","progress","responsechunk","bloboutput","usage","complete","error"],"description":"Name carried on the `event:` line."},"data":{"description":"The event payload. For `error` the payload shape says where the\nfailure happened. The agent service reports `{code, message, source?}`\n(for example `tool_stream_error` / `Tool execution failed` with\n`source: tools`) and the stream goes on to `usage` and `complete`\nwith `success: false`. The chat relay reports `{content: {code,\nmessage}}` and ends the stream there — no `usage`, no `complete`\n(`validation_error` for a `conversationHistoryId` over 200\ncharacters). A stream that broke before reaching the relay\narrives as a `data:` line with no `event:` line whose JSON carries\n`error` or `code`.","oneOf":[{"type":"object","additionalProperties":true},{"type":"string"}]}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"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"}}}},"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/aiagents/chat":{"post":{"operationId":"chatWithVirtualAiAgent","tags":["AI Agents"],"summary":"Chat with an unsaved AI agent document","description":"Sends one message to an AI agent document carried in the request — a\ndraft never saved, or a saved agent with unsaved changes — and streams\nthe answer back as `text/event-stream` with the same events as\n`POST /v1/imports/{_id}/aiagents/chat` (see `ChatStreamEvent`). The\ndocument is validated in memory the way a save validates it and is\nnever persisted: the saved agent, if there is one, is untouched, so\nthis is how a change is tried before it is saved. The agent's tools\nexecute for real and the turn is billed; none of the flow pipeline\nruns. `conversationHistoryId` keeps a conversation keyed on the\ndocument's `_id` when one is present, so a draft of a saved agent can\ncontinue that agent's history. Needs the `ai_agent:virtual_chat`\npermission (manage and administrator roles; monitor gets 403) and\n`Content-Type: application/json` (415 otherwise).\n\nThe integrator.io agent editor uses this route for a Test or chat\nmessage while the form has unsaved edits. A tile-level manager can use\nit only when every connection, MCP connection and Celigo Tool the\ndocument references is inside accessible integrations. A turn the\nagent service could not finish still answers 200 with an `error` event\ninside the stream.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VirtualChatRequest"}}}},"responses":{"200":{"description":"The turn as a stream of server-sent events, ending with `complete`.\nA turn the agent service could not finish still answers 200: the\nfailure arrives inside the stream as an `error` event.","content":{"text/event-stream":{"itemSchema":{"$ref":"#/components/schemas/ChatStreamEvent"}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"415":{"description":"`Content-Type: application/json` header is missing."},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
````
