Skip to content

Example imports

Four n8n workflows and one Dify app to import into Studio, and — more usefully — what each one actually produces. Every figure below was measured by running the file through the importer, not estimated. The Dify example needs 0.9.0 or later: 0.8.0 and earlier answer a Dify import with 501 not_implemented.

Import one from the SPA, or over the API:

curl -X POST http://127.0.0.1:8091/api/projects/default/import/n8n \
  -H 'Content-Type: application/json' \
  -d "$(jq -n --rawfile raw examples/n8n/chat-agent.json '{raw: $raw}')"

On a project that is already bound to a platform environment, that answers 422 alias_required. A bound project has an approved generation, so an import has to say which alias it belongs to — add alias beside raw:

  -d "$(jq -n --rawfile raw examples/n8n/chat-agent.json \
        '{raw: $raw, alias: "default"}')"

The alias must name one of the project's environments — default is the one the zero-config binding creates; any other name answers 422 unknown_alias. The command above is written for a fresh instance, where default is unbound. The two are exclusive in the other direction too: an alias on an unbound project is 409 project_unbound, because there is no approved generation for it to name.

File Blocking gaps Manual gaps Info notes Converts to
n8n/chat-agent.json none entry_mechanism_not_carried (the trigger) the iteration bound a one-agent spec
n8n/hello-agent.json 1 (source_cannot_run) entry_mechanism_not_carried (the trigger) the iteration bound a one-agent spec n8n itself never ran
n8n/agent-with-tool.json 2 (source_cannot_run, tool_without_backing) entry_mechanism_not_carried (the trigger) the iteration bound a one-agent spec, with the tool left out and carded
n8n/needs-work.json 3 entry_mechanism_not_carried (the trigger) — nothing usable — every step is unrepresentable

The one thing to know first

Every n8n workflow's trigger becomes a card, and that is normal. Kneo has no trigger concept, so the card says "Trigger must be recreated outside Kneo". For a webhook, chat, schedule or event trigger it is manual, because the route or schedule is real behaviour to recreate; for a manual trigger it is info. A first import showing that one manual gap, and otherwise only info notes, is the clean outcome. An info note asks nothing of you: here it says the agent's tool loop runs with n8n's default iteration bound, which the spec leaves to the platform's default. chat-agent.json exists so you have something to compare against, because "one gap" is otherwise indistinguishable from "something went wrong".

Why hello-agent.json blocks

It is a workflow n8n itself refuses to run, and Studio says so rather than handing you a working agent the source never was. A schedule starts it, and the agent's prompt is auto, which reads the incoming item's chatInput, but a schedule's item has no chatInput. On top of that, an OpenAI model under n8n's tools agent needs at least one tool, and it has none. The pinned n8n fails that agent on every item, so the card is source_cannot_run, and the fix belongs in the source. chat-agent.json is the same agent made runnable. A chat trigger supplies chatInput, and an Anthropic model needs no tool. agent-with-tool.json has the first defect too, because a webhook's item carries the request under body, not chatInput.

What converts

Measured against the pinned release (n8n@1.62.1 — see N8N_COMPAT):

Converts. Agent nodes and the language model attached to one, where n8n itself can run the agent: an agent n8n fails on every item — no model, a model its executor refuses, or an auto prompt with no chatInput to read — is a blocking source_cannot_run card instead. Several agents in sequence become a sequential workflow — each agent after the first needs a defined prompt, since an auto prompt after an agent has no chatInput to read. An if between agents becomes a graph workflow, so the branching shape survives.

Tools do not convert, and this is the limitation to know about. A Kneo tool is backed by exactly one of a Python implementation import path, a declared MCP server, or another agent — and an n8n export describes none of them. A toolHttpRequest names an ordinary REST endpoint, which is not an MCP server: pointing Kneo at it would send MCP protocol traffic to your API. A toolCode is JavaScript, where an implementation is a Python import path.

So every ai_tool attachment that is a tool node (toolHttpRequest, toolCode) becomes a blocking card naming the tool, and the tool is left out of the spec. Studio previously emitted it with an empty backing, which the platform refused outright while the import reported success — a draft that could not be governed and nothing saying why.

To carry a tool across, add it to the spec yourself with a real backing after the import. An n8n MCP client node is the shape Kneo could express directly, but it is not among the node types Studio converts at the pinned n8n@1.62.1, so it imports as a blocking proprietary_node card today.

The other ai_tool case is blocking too: n8n's agent as tool pattern, where a sub-agent is attached to a main agent to delegate to. Kneo has no form for it. The sub-agent imports as a separate step with nothing wired to it, and draws three blocking cards — "Offered as a tool, imported as a step", "The source's trigger never reaches this node", and a second-entry-point card. The source let the main agent decide whether to call it; decide whether the delegation mattered, then wire it in or remove it.

Does not convert. Ordinary processing steps — httpRequest, code, and anything outside the pinned node list — each become a blocking proprietary_node gap: "No Kneo equivalent". This is the part worth internalising before importing something real:

A Kneo spec is agent-centric. An n8n workflow that is mostly plain steps — fetch, transform, branch, write — has almost nothing to map onto, and will import as a wall of blocking gaps. That is the importer being honest, not broken. The workflows that convert well are the ones where an agent is doing the work and the other nodes are feeding it.

Two partial cases, both blocking:

  • an if condition is compiled by n8n to an expression Kneo cannot carry, so the branch shape converts and the condition becomes an expression gap;
  • a credential written into a node — needs-work.json puts one in an Authorization header — becomes an inline_secret gap. The value is not carried into the spec; bind a credential reference instead.

Why needs-work.json is here

A realistic first import often looks like this one, and seeing three blocking gaps with no reference point reads as failure. It is the honest answer for a workflow with no agent in it: Fetch records and Vendor widget have no Kneo equivalent, and the header carries a secret.

Import it after chat-agent.json, so the difference is visible rather than alarming. Its blocking gaps are what stops it exporting — that gate is the product working.

The API key in it is a placeholder (sk-live-NOT-A-REAL-KEY) and matches nothing.

A Dify app: dify/answer-workflow.yml

A workflow app in the shape Dify's Export DSL writes (DSL 0.7.0): a start form asking for a question, one LLM answering it, and an end node returning the answer. Import it with Source: Dify, or over the API at /import/dify, with the same body and the same alias rules as above:

curl -X POST http://127.0.0.1:8091/api/projects/default/import/dify \
  -H 'Content-Type: application/json' \
  -d "$(jq -n --rawfile raw examples/dify/answer-workflow.yml '{raw: $raw}')"
File Blocking gaps Manual gaps Info notes Converts to
dify/answer-workflow.yml none run_input ×2 (the start form, the LLM's user message), output_formatting_not_carried (the end node's named output) the start a one-agent spec: the model and the system prompt

On Dify the start's card is info, not manual: a start is how a caller begins a run, which a Kneo run already is. What a clean Dify import asks of you instead is run input — what each caller now supplies — and the response format. This one is clean because an LLM does all the work. Most real Dify apps are not: over 614 real exports, 607 convert and 43 of those carry no blocking card. What blocks the rest is in Importing a Dify app.

Subsections