Migrate Savant Analyses
PlaidCloud converts a Savant Labs analysis export into a runnable Advanced workflow. The converter maps each Savant agent to a workflow step, a project variable, or a canvas object, keeps the original graph, and returns a conversion report that names anything it could not convert.
Conversion reads only the export file you point it at. It never contacts the system a Savant source or destination connects to.
What PlaidCloud Creates
Section titled “What PlaidCloud Creates”- Each Savant agent becomes the matching workflow step, with upstream and downstream relationships preserved. The Savant Conversion Matrix lists every agent, its mapping tier, and any option without a mapping.
- Savant analysis parameters become project variables, and references to them inside agent settings are rewritten to the PlaidCloud variable syntax.
- Notes and groups arrive on the canvas as notes and groups.
- Each source becomes an import step, or reads an existing table, depending on how you bind it.
- Every Savant agent type converts. A configuration the converter can’t reproduce arrives as a named placeholder step. See Placeholders for Unconverted Configurations.
- A step whose Savant behavior the help documentation doesn’t spell out is converted to the most likely reading and carries a semantics assumed note in the conversion report. Check those steps against your Savant output.
Before You Start
Section titled “Before You Start”- Export the analysis from Savant with Export JSON.
- Upload the
.jsonfile to a Document account you can read. - Choose the PlaidCloud project that should receive the workflow. You need write access to it.
- Choose a Document account and base folder where the analysis’s source files will be read from.
Convert an Analysis
Section titled “Convert an Analysis”Convert from the Analyze app, the REST API, or the MCP tool catalog. All three take the same fields.
Analyze App
Section titled “Analyze App”- Open Analyze and choose Tools → Import Savant Analysis.
- Choose the Target Project and the Savant Export File (.json) you uploaded to a Document account.
- Select Load Sources. The window lists each source node in the export.
- To connect a source to data you already have, select it and choose Bind…. Choose Project table to read a table in the target project, or Document to read a file from a Document account (with an optional worksheet name for Excel). Choose Unbind to remove a binding.
- Under Unbound Sources Directory, choose the Document account and base folder where files for unbound sources are read from.
- Under Conversion Options, choose the Workflow Type and whether to Allow unconverted placeholders. Under Object Prefixes, optionally set workflow, step, and table prefixes.
- Select Convert.
When conversion finishes, the window shows the conversion report: the workflow name and table prefix, the warnings, a table of the Document paths to upload files to for unbound sources, and the steps that need review. If the list of sources can’t be loaded, add a row for each source with its node id and the analyzetable_ id of the table to bind.
Fields
Section titled “Fields”The REST API and MCP tool take these fields.
| Field | Required | What it does |
|---|---|---|
source_account_id |
Yes | Document account that holds the Savant export. |
source_file_path |
Yes | Path of the export within that account. |
project_id |
Yes | Project where the workflow is created. |
document_account_id |
Yes | Document account that unbound sources read from. |
document_account_path |
Yes | Base folder in that account for unbound sources. An empty value or / is the account root, and a leading / is ignored. Paths with .. or dot-led segments, braces, a drive letter or URL scheme, or control characters are refused. |
workflow_prefix |
No | Prefix added to workflow names. |
step_prefix |
No | Prefix added to step names. |
table_prefix |
No | Prefix added to table names. Defaults to the workflow’s own name. A prefix that would reuse an existing table name is refused before anything is created. The prefix used is returned in the conversion report. |
workflow_type |
No | advanced (default) lays the graph onto the DAG canvas. standard produces a serial step list. |
source_bindings |
No | Binds sources to existing data. See Bind Sources. |
destination_bindings |
No | Routes destinations to a document account, a connection table, a Google Sheet, a Tableau data source, a Slack channel, or email recipients. REST and MCP only. See Destinations. |
api_bindings |
No | Says where an API agent’s relative URL goes: a base URL or a REST connection. REST and MCP only. See Bind API Requests. |
allow_unconverted_placeholders |
No | Controls what a placeholder does at run time. See Placeholders for Unconverted Configurations. |
Send the fields as a JSON body to:
POST /rest/v1/analyze/savant/convert{ "source_account_id": "<document account id>", "source_file_path": "/savant/quarterly-margin.json", "project_id": "<project id>", "document_account_id": "<document account id>", "document_account_path": "/savant-inputs"}To list an export’s source nodes before you build source_bindings, send source_account_id and source_file_path to:
POST /rest/v1/analyze/savant/sourcesEach entry in the response has the source’s node_id, name, and connector.
An MCP-connected AI agent calls the savant_convert tool with the same fields. See AI Agents (MCP) to connect an agent. The tool changes your project, so review the call before you approve it.
Convert-Time Bindings
Section titled “Convert-Time Bindings”A Savant export carries no data, no credentials and no connections, so three convert-time parameters connect it to yours. Each is a map from a Savant node id or name to a target, and a binding you can’t use refuses the whole conversion before anything is created.
| Parameter | Binds | Section |
|---|---|---|
source_bindings |
Source agents to tables or files | Bind Sources |
destination_bindings |
Destination agents to a file, table, sheet, Tableau data source, Slack channel, or email | Destinations |
api_bindings |
API agents with a relative URL to a base URL or REST connection | Bind API Requests |
Bind Sources
Section titled “Bind Sources”A Savant export describes where its data comes from but carries no data and no credentials. Use source_bindings to connect each source to data you already have in PlaidCloud. A key is the source’s node id, dataset id, or name. A value is either:
- An
analyzetable_id for a table in the target project. The converted workflow reads that table directly. - An object with
account,path, and optionallysheet, to read a file from a Document account.
"source_bindings": { "Sales": "analyzetable_1234", "Budget": { "account": "<document account id>", "path": "/finance/budget.xlsx", "sheet": "FY27" }}A binding you can’t read is refused. A key that matches no source is reported as a warning.
Unbound Sources
Section titled “Unbound Sources”A source with no binding becomes an import step with no data yet, and the conversion report carries a warning for it. The step reads from:
<document_account_path>/savant/<workflow id>/<source name>.csvUpload the file to that path and run the workflow. Until the file is there, the import step finishes with a warning and creates an empty table, and the workflow continues.
How Agents Convert
Section titled “How Agents Convert”Most agents convert to the step you would build by hand; the Savant Conversion Matrix lists every agent. These agents need extra care.
| Savant Agent | What It Becomes | Caveats |
|---|---|---|
Infer Agent (gen_ai, and legacy service with an LLM service) |
An AI/NLP step with the Prompt task. The prompt runs once per row over the input fields, and the answer lands in an AI Answer column beside every input column. A row limit becomes Max rows, and an empty source warns and writes an empty table. | An LLM answers, so output is never byte-identical to Savant’s and can vary between runs. Each call has a cost. See AI Providers. A batch that fails keeps its rows with an empty answer and warns. |
Vision Agent (vision) |
A Document Read Blob step, Image OCR, then the AI/NLP Prompt task. Each document goes to the provider as an image or PDF when the provider accepts that type and size. Otherwise the step sends its PDF text layer, or the OCR text. | Same LLM caveats as the Infer Agent. Whole documents leave PlaidCloud for the provider, and the conversion warns. TIFF and BMP files are sent as OCR text, and there is no maximum document size setting. A document that isn’t found is skipped and the step warns with its path. |
API Agent (apiService, and legacy service with an API service) |
A REST Request step that sends one request per input row, then a join back that keeps every input row. The raw response body lands in a text column, with a status column beside it. A quota per period becomes a fractional Max req/sec. A relative URL joins the base URL or REST connection you bind with api_bindings. A host, port, or path that comes from a row field is filled in per row. Next-URL, Link header, offset, and page paging are followed. |
A method other than GET runs against the live system once per input row every time the workflow runs, so confirm the target before the first run. Non-GET requests aren’t retried automatically. A failed request stays as a row with its status. A request that carries a credential can’t take its host from a row field. Credentials are handled as described in API Steps with Credentials. |
Recursion Agent (hierarchy) |
A Hierarchy Closure step that walks each row up its parent chain to any depth, then aggregates each node over the ids on its path to the root, itself included. It writes Depth and Path. A row with a blank parent, or naming itself as its parent, is a root. | A loop in the parent ids stops at the repeated node and keeps every ancestor before it. Rows with several parents each keep their own values. |
Search and Replace (search_replace) |
A Table Extract that applies each rule in order to the chosen text columns, case-sensitive or not, whole word or not. Each rule runs in its mode: replace the matched text, replace the whole cell when it contains the text, replace the whole cell when it matches exactly, or append to the cell. | Rules are literal text, not patterns. |
Split (split) |
A Split Text step. It splits into columns or rows on a literal separator of any length, and decides the number of columns from the data when the workflow runs. | None beyond the matrix status. |
Time Series (rollup) |
A Table Extract that groups by a period expression, with a Period Spine, an anti join, and a union that zero-fill the periods with no records. | Records with a NULL date are excluded. The spine stops at 100,000 periods. |
Format (format) |
A Table Extract with the renames, retypes, hides, and fills, or a Promote Header step for replace-names-with-row. | A few Format settings aren’t converted. See What Stays Limited. |
Spatial Match Agent (spatial_match) |
A Spatial Match (executor) step for intersects, within, contains, touches, crosses, overlaps, equals, and disjoint, with several rules combined; Cross Join for a cross match; Find Nearest for a nearest match. | A nearest match ranks and filters by great-circle distance, as Savant does. A nearest rule combined with other rules finds the nearest records first, then applies the other rules: with AND the result keeps records that satisfy every rule, and with OR it unions the nearest records with the records the other rules match. A nearest rule with neither a count nor a maximum distance keeps the single nearest record. A within-distance match converts too, in kilometers when the rule gives no unit, and tests WKT points only. |
Spatial Summarize Agent (spatial_summarize) |
Spatial Combine for union, intersect, and bounding box, plus Spatial Centroid, Spatial Point to Line, and Spatial Line to Polygon for geometric center and line or polygon builds. | A polygon built from fewer than 3 points is an empty polygon. |
Destination Agent (destination) |
An export step to the target you bind, or a CSV when you don’t. See Destinations. | See What Stays Limited. |
GEO_POINT, GEO_TYPE, and GEO_SPATIAL_DISTANCE convert to expressions. An edit whose whole expression is another GEO_* function, such as GEO_AREA or GEO_UNION, converts to the matching spatial step, including Spatial Info. Geometry travels as WKT text. GEO_AREA, GEO_PERIMETER, and GEO_SPATIAL_DISTANCE in miles use the statute mile.
A source that reads binary files, such as the documents a Vision agent reads, converts to a document extract of the bound document, or of the file expected at the unbound sources path.
AI Providers
Section titled “AI Providers”An Infer or Vision agent converts to an AI/NLP step that uses an LLM connection. The converter supports Anthropic, OpenAI, Gemini, Azure OpenAI, Grok, and GitHub Models.
- The converter picks the connection for the provider and model the agent names, from the LLM connections the importing user can see. It never names one the user can’t see. Through an AI assistant (MCP), that means the connections the assistant’s user can see.
- When the provider has no visible connection, or Savant’s provider has no PlaidCloud match (a cloud-hosted model API or a bring-your-own-key provider of the Savant workspace), the step uses the workspace default connection if the user can see it. Otherwise it leaves the connection blank, and the step uses the workspace default when it runs. The conversion report warns either way, so choose a connection on the step if a different one should answer.
Destinations
Section titled “Destinations”A Savant destination writes the table it receives to the target you bind. Bind destinations with destination_bindings in the REST body or MCP call. A key is the destination’s node id or name. The Analyze window doesn’t bind destinations, so a destination converted there writes a CSV.
| Target | Binding | Result |
|---|---|---|
| Document account | {account, path[, file_name]} |
A CSV, TSV, TXT, Excel, Parquet, JSON, Avro, XML, PDF, or Tableau Hyper file, by the kind of destination. You need the document storage write permission on the account. |
| Table on a connection | {connection, table[, schema, replace]} |
An Export: External Database Tables step. You need connection write permission. Replacing the table needs replace: true. |
| Google Sheets | {connection, spreadsheet_name[, sheet_name, replace]}, or {connection, spreadsheet_id[, sheet_name, replace]} |
An Export to Google Spreadsheet step on a Google Sheets connection. The tab defaults to the one the destination names, else Sheet1. A destination that replaces the sheet’s rows clears the tab, so it needs replace: true. |
| Tableau | {account, connection[, path, file_name, replace, datasource_name, tableau_project]} |
A Tableau Hyper extract written to the account’s folder, then a Publish to Tableau step on a Tableau connection. Replacing the data source, or changing its rows on match keys, needs replace: true. |
| Slack | {connection[, channel]} |
The CSV is written to a document account, then a Notify via Slack step uploads it to the channel. The connection needs a bot token, and the channel must be a channel ID, taken from the binding or else from the export. |
{email_to: [...]}, and email_export_recipients: true to also use the recipients the export lists |
The CSV is mailed as an attachment. You need email write permission. |
A Sheets, Tableau, or Slack connection is checked the way a table connection is: you must be able to see it and hold connection write permission, or the conversion is refused.
Write Modes
Section titled “Write Modes”- A file or table destination that appends reads the earlier file back and rewrites it with the new rows. It never overwrites.
- An upsert, update, or groupReplace destination merges on the destination’s match keys. A file or table is read back and rewritten, and a Tableau data source has its matching rows changed in place.
- A Tableau destination maps Savant’s modes to the six Publish to Tableau modes: append and insert add rows, replace overwrites, and upsert, update, and groupReplace match on keys. A mode that changes existing data publishes only when the binding says
replace: true; otherwise the destination publishes a new data source and the report says so. - A destination with an output encoding writes the file in that Java character set. A character the set lacks is written as
?, as Savant does. - Export file names must stay inside the bound folder.
What AI Callers Can Bind
Section titled “What AI Callers Can Bind”A conversion through an AI assistant (MCP) binds the same shapes with these parts withheld, each with a warning that names the part:
| Withheld | What Happens Instead |
|---|---|
replace on a table, sheet, or Tableau data source |
A destination that appends still appends to its bound table or sheet. A destination that replaces writes a CSV. A Tableau destination publishes a new data source. |
| Email recipients | A CSV is written in place of the email. |
| Slack | A CSV is written in place of the Slack post. |
Convert from the Analyze app or the REST API, or sign in yourself rather than through an assistant, to bind these.
A destination with no usable binding writes a CSV to the Document account you gave for unbound sources and warns Fallback, target not written, with the original target in the message. Nothing is dropped. The CSV is not the destination’s own output, so check it before you rely on it. A plain CSV destination writes its own file.
Bind API Requests
Section titled “Bind API Requests”An API agent whose URL is relative, such as /v2/customers, has no host to send to. Bind one with api_bindings. A key is the API node’s id or name, or * for every API node without a binding of its own. A value is one of:
- A base URL, as a string or
{"base_url": "https://host/path"}. The relative URL is joined to it. The URL must behttporhttpswith a public host, and carry no user, query, or fragment. {"connection": "<REST connection id>"}. The request goes to that REST connection’s host and carries its credentials. You need connection write permission, and the connection must be a REST connection you can see.
"api_bindings": { "*": "https://api.example.com/v1", "Customers": { "connection": "<REST connection id>" }}An unmatched key is a warning, and a binding you can’t use is refused. A relative URL with no binding stays a named placeholder.
An AI assistant (MCP) without connection write permission has a connection binding ignored with a warning, and the relative URL it would have joined stays a placeholder. Bind a base URL, or convert from the Analyze app or the REST API.
A host, port, or path that comes from an input field needs no binding. It is filled in per row, each value checked as it is used, and the report lists the hosts a row may name. A request that carries a credential can’t take its host from a row field: sending a credential to a host the data chooses is refused.
Paging converts too. A next-page URL in the response, a Link header, an offset, and a page number each become a paging mode on the REST Request step, which reads every page.
API Steps with Credentials
Section titled “API Steps with Credentials”A credential in an API agent’s headers, URL path, query, or body moves into a PlaidCloud REST connection that the import creates. The REST Request step references that connection, so the credential never sits in the step.
- The response lists the connections the import created or reused under
connections, and the import report names them. Importing the same analysis again reuses the same connection rather than creating another. - Credentials in the URL user info and in headers go into the connection’s own authentication fields or default headers. Path, query, and body secrets are stored encrypted on the connection and filled in at run time, only for requests to that connection’s own
httpshost. - An unencrypted
http://endpoint is not converted with credentials. The credential is left out and the conversion report warns that it was not copied. - Importing through an AI assistant (MCP) does not create connections. Credentials are left out and a warning says so. Import from the Analyze app or the REST API instead, or add the credential to a REST connection by hand and select it on the step.
- A user without connection-write permission gets the same outcome: credentials left out and a warning.
- A pipeline parameter used where an API credential goes, in a header, query, path, or body, moves into the connection’s encrypted
secret_values, and the step refers to it through the connection. It is never created as a workflow variable, whatever the parameter is named. - A secret typed into a formula, filter, or replace value is left out of the step and the conversion report warns. A path, a URL, and a 40 or 64 character hex digest are kept, unless part of them or a secret name says otherwise. Folder sharing links and recipient email addresses never appear in warnings or notes.
- A response that echoes a credential back is stored as received. Treat the output table of such a request as sensitive.
JWT_ENCODE
Section titled “JWT_ENCODE”JWT_ENCODE converts to a JWT Encode step. A literal signing key goes into a dedicated connection bound to the step, and the token is signed at run time, so the key never appears in the step configuration. Through an AI assistant, or without connection-write permission, the key is left out, the token is NULL, and a warning tells you to import again with permission to create connections.
Promote a Data Row to Column Headers
Section titled “Promote a Data Row to Column Headers”A Format agent set to replace names with row converts to a Promote Header step. At run time the chosen row, counted from 1, becomes the column names and is removed. A source that carries no record order gets a note, because which row names the columns can change from run to run. A column that row names but no later step mentions is not carried past a step that lists the columns it reads.
Files with No Declared Columns
Section titled “Files with No Declared Columns”A source with no declared columns keeps these columns:
- A CSV, TSV, or TXT source bound at import keeps every column in the file’s header.
- Any other file type, and any unbound source, keeps only the columns later steps name, and the conversion report warns.
When such a CSV feeds only a promoted header, the import is an Import Delimited step that reads every column by position. A step that exports or joins after a promoted header carries every column the table has at run time (see Also carry columns named at run time in Table Extract).
Behaviors Worth Knowing
Section titled “Behaviors Worth Knowing”These conversions behave as follows.
| Agent | Behavior |
|---|---|
Blend (blend) |
Joins on OR-combined predicates, contains, and is-part-of. Outer-excluding-inner blends and the unmatched outputs convert to the matching join streams. For any operator, the duplicate right-hand key is dropped from the output. |
Dedupe (deduplicate) |
Keeps the first record per key within each Group By group. The duplicates flag marks every member of a duplicate group, and duplicates can also route to a second stream. With a single outlet connected, it behaves as Savant does. |
Summarize (summarize) |
Window aggregates add the aggregate to every row instead of collapsing groups. |
Sample (sample) |
First N rows, per group or overall, a random percent or number of rows, or one row in every Nth. |
Time Series (rollup) |
Day, week, month, quarter, year, and decade periods between the optional start and end dates, per group. Month periods are labeled YYYY-MM, and periods with no records appear with zeros. Records with a NULL date are excluded. |
Pivot (pivot) |
COUNT counts rows. |
XML (xml) |
SELECT extracts elements, paths can carry namespaces, and TO_JSON converts XML to JSON. Any XPath 1.0 path converts, including predicates, attribute tests, wildcards, and functions such as count(), name(), and contains(), and the XML Parse step reads it as XPath. With processNs off, names are matched literally, prefix included. |
JSON (json) |
Flattens into wide columns. Text that isn’t valid JSON gives NULL, and arrays come out as text. |
Split (split) |
Splits into columns or rows, with the number of columns read from the data each time the workflow runs. |
These expression functions follow Savant’s results:
| Function | Behavior |
|---|---|
DATE_DIFF |
Weeks and quarters count the way Savant does. |
PAD |
Doesn’t truncate a value longer than the target width. |
+ on text |
Concatenates text. A column from a source with no declared types reads as a number, so + adds, and a note says so. |
| Split to rows | Keeps empty pieces as rows. |
LAST with no sort |
Returns the last row in the order rows arrive, with a note that the warehouse doesn’t guarantee that order. |
JOIN_STR |
Joins a fractional number as Savant writes it, without trailing zero padding. |
JSON_FORMAT |
Pretty-prints the JSON. |
JSON_SET |
Keeps Savant’s key order. |
GREATEST and LEAST |
Skip NULL arguments. The result is NULL only when every argument is NULL. |
ROUNDUP |
Rounds up to the next whole step, so a negative number rounds toward zero: -1.5 becomes -1. ROUNDDOWN is unchanged. |
DURING_DAYLIGHT_SAVING and DURING_STANDARD |
NULL in gives NULL out. |
TO_TEXT with a date format |
Reads the format tokens as Savant does. YYYY, w, and u are the ISO week-year, week, and weekday, and a zone or era letter is written as for a UTC time. |
TO_TEXT and number-to-text output |
Writes plain numbers such as 12, not 12.0000000000. |
TO_TEXT with a numeric pattern |
Formats as a Java DecimalFormat does, rounding half to even, including scientific patterns such as ##0.##E0. |
TO_TEXT and date parsing with a per-row format |
Reads the format from each row. |
| Fractional-second, narrow month, and week-based date tokens | Convert. The week tokens read as ISO weeks, with a semantics assumed note. |
TO_TEXT and date formats convert in a Text Function step. The converter refuses a plain aggregate or window function inside an ordinary expression and flags it by name, because Savant itself fails on it at run time. Inside a calculation or a Summarize it converts.
Expression functions also convert for sha1, NOT_TRUE, LEVENSHTEIN, the daylight saving time functions (DST), and regular expression functions that take an nth match. DAY_OF_WEEK numbers Sunday as 7, and DAY_NAME returns the day’s name.
Read the Conversion Report
Section titled “Read the Conversion Report”The response carries the conversion report: a summary of how many steps converted with high, medium, or low confidence and how many did not convert, plus warnings and the nodes the converter absorbed into others. Read it before you run the workflow. It names every placeholder, every unbound source and unmatched binding, and the table prefix used.
Placeholders for Unconverted Configurations
Section titled “Placeholders for Unconverted Configurations”A configuration the converter can’t reproduce is created as a placeholder step that carries the agent’s name, and the conversion report names it. A placeholder appears for a construct listed in What Stays Limited or one the export doesn’t describe fully, such as a missing API configuration. By default a placeholder fails when the workflow reaches it, so a run can’t report success on work that never happened.
Set allow_unconverted_placeholders to true to import each placeholder as a pass-through step instead. The run then continues past it and the placeholder’s output is its input. Rebuild the capability with a native step when its result matters.
Run the Converted Workflow
Section titled “Run the Converted Workflow”- Open the new workflow in the project.
- Set any project variables that came from Savant parameters.
- Upload files for unbound sources to the paths in the report.
- Run the workflow and review each step’s output table.
What Stays Limited
Section titled “What Stays Limited”A converted workflow can differ from Savant in these ways. The conversion report names each one that applies.
Reading differences. Savant’s help doesn’t settle these, so the converter takes one reading, and the converted step can differ from the Savant answer:
- Ties between equal values. In a nearest match, two candidates can sit at the same distance. Savant picks the one that arrived first. A warehouse join has no arrival order, so the converted step can pick the other.
- The distance column. The converted nearest match writes a
Distancecolumn beside each match. Savant writes none.
Language models. Infer and Vision agents depend on a language model, so their answers differ from Savant’s and between runs. A Fuse agent matches with the deterministic Fuzzy Match step at a threshold of 80, where Savant uses a language model.
Destinations that fall back to a CSV. A destination with no binding, one whose binding the conversion can’t use, a replace that the binding doesn’t allow, a Slack or email part an AI assistant withheld, and a sheet whose tab name comes from a field fall back to a CSV with a warning. See Destinations.
Other limits.
- Date and timestamp conversions of text that isn’t in a recognizable date format can fail the run on that row.
- A source with no declared columns that isn’t a bound CSV, TSV, or TXT file keeps only the columns later steps reference. See Files with No Declared Columns.
- An API agent whose URL is relative needs an
api_bindingsentry, and a request that carries a credential can’t take its host from a row field. Credentials in anhttp://URL are left out, and MCP imports don’t create connections. - A Vision agent sends TIFF and BMP files as OCR text.
- Format settings for sort, field reordering, extra, and replace aren’t converted, and fill writes only text columns.
- An XML path that isn’t XPath 1.0 becomes a placeholder.
- A spatial within-distance test reads WKT points only.
- On StarRocks and Snowflake,
JSON_FORMATandJSON_SETfail at run time, and the conversion report notes it.