Wiring an Artifact-Capable Workflow
The six artifact tools — docx_read, xlsx_read, pdf_read, docx_write, xlsx_write, pdf_write — are not enabled by default on every agent. A workflow opts in by declaring each tool as a node and edging it to a deep_agent node with the tools handle. This page covers exactly that wiring.
If you are building a workflow in the visual editor, the editor renders these nodes and edges directly — you do not need to write the JSON by hand. The structure below is what gets stored.
Anatomy of a tool-bearing workflow
Every artifact-capable workflow has the same four ingredients:
- An
agent_inputnode — acceptsuser_prompt(and optionallysession_id) from the calling Responses API request. - A
deep_agentnode — the LLM-driven orchestrator. Receives the user prompt and decides which tools to call. - One tool node per artifact tool —
docx_read,pdf_write, etc. Each is its own first-class node withisTool: true. toolsedges — fromdeep_agent(handletools) to each tool node (handletool). This is the line that tells the runtime "this tool is available to this agent."
The tools edge is the only thing that gates availability. There is no separate allowlist, no per-agent permission table — if the edge exists, the tool is callable; if it does not, the tool simply is not part of the agent's tool list.
Node and edge shape
A tool node
A tool node is minimal: a stable id, a type from the artifact tool names, and data.isTool: true. The runtime instantiates the corresponding worker node class on demand.
{
"id": "docxWrite-8",
"type": "docx_write",
"position": { "x": 1250, "y": 280 },
"data": {
"label": "DOCX Write",
"isTool": true,
"params": {},
"handles": ["tool"],
"isInput": false,
"isOutput": true,
"inputs": [],
"outputs": [],
"errors": []
}
}
All six tools use the exact same shape — only the id, type, and label change.
A tools edge
{
"id": "e-deep-wDocx",
"source": "deepAgent-4",
"target": "docxWrite-8",
"sourceHandle": "tools",
"targetHandle": "tool"
}
sourceHandle: "tools" and targetHandle: "tool" are the magic strings. Any other handle combination is treated as a data-flow edge and the runtime will not register the target as a tool.
The deep_agent declaration
The deep agent itself is an ordinary node, with one detail that matters for tooling — its handles must include "tools":
{
"id": "deepAgent-4",
"type": "deep_agent",
"data": {
"params": {
"model": { "value": "claude-sonnet-4-6", "isExpression": false },
"system_prompt": { "value": "You are a document transformation agent...", "isExpression": false },
"user_prompt": { "value": "@agentInput-3.user_prompt", "isExpression": true, "isAttachedToInputNode": true },
"streaming": { "value": true, "isExpression": false }
},
"handles": ["inputs", "outputs", "tools", "agents"]
}
}
Tools are discovered at compile time by walking out_edges(deep_agent_id, sourceHandle="tools") and looking up each target's type in the worker NODE_MAPPING. Unknown types fail loudly at workflow validation, not silently at run time.
A complete worked example
Below is the full topology used by the end-to-end template round-trip test (sandbox/test-artifacts/create_template_workflow.py) — one deep agent, all six artifact tools, an input, and an output.
{
"name": "document-transformer",
"type": "streaming",
"nodes": [
{ "id": "agentInput-3", "type": "agent_input", "data": { "isInput": true, "handles": ["outputs"] } },
{ "id": "deepAgent-4", "type": "deep_agent", "data": { "handles": ["inputs","outputs","tools","agents"], "params": { "model": { "value": "claude-sonnet-4-6" } } } },
{ "id": "docxRead-5", "type": "docx_read", "data": { "isTool": true, "handles": ["tool"] } },
{ "id": "xlsxRead-6", "type": "xlsx_read", "data": { "isTool": true, "handles": ["tool"] } },
{ "id": "pdfRead-7", "type": "pdf_read", "data": { "isTool": true, "handles": ["tool"] } },
{ "id": "docxWrite-8", "type": "docx_write", "data": { "isTool": true, "handles": ["tool"] } },
{ "id": "xlsxWrite-9", "type": "xlsx_write", "data": { "isTool": true, "handles": ["tool"] } },
{ "id": "pdfWrite-10", "type": "pdf_write", "data": { "isTool": true, "handles": ["tool"] } },
{ "id": "agentOutput-11","type": "agent_output", "data": { "isOutput": true, "handles": ["inputs"] } }
],
"edges": [
{ "source": "agentInput-3", "target": "deepAgent-4", "sourceHandle": "outputs", "targetHandle": "inputs" },
{ "source": "deepAgent-4", "target": "docxRead-5", "sourceHandle": "tools", "targetHandle": "tool" },
{ "source": "deepAgent-4", "target": "xlsxRead-6", "sourceHandle": "tools", "targetHandle": "tool" },
{ "source": "deepAgent-4", "target": "pdfRead-7", "sourceHandle": "tools", "targetHandle": "tool" },
{ "source": "deepAgent-4", "target": "docxWrite-8", "sourceHandle": "tools", "targetHandle": "tool" },
{ "source": "deepAgent-4", "target": "xlsxWrite-9", "sourceHandle": "tools", "targetHandle": "tool" },
{ "source": "deepAgent-4", "target": "pdfWrite-10", "sourceHandle": "tools", "targetHandle": "tool" },
{ "source": "deepAgent-4", "target": "agentOutput-11", "sourceHandle": "outputs", "targetHandle": "inputs" }
]
}
This single workflow lets the agent read three input templates, transform them, and emit three new output documents in one Responses API turn.
Choosing which tools to expose
The six tools are independent — add only the ones a workflow actually needs.
| Use case | Minimum tools to wire |
|---|---|
| Agent reads a docx and summarises it | docx_read |
| Agent generates a PDF report from a prompt | pdf_write |
| Agent fills an XLSX template with computed data | xlsx_read, xlsx_write |
| Document round-trip across all three formats | All six |
Fewer tools shorten the agent's tool-selection prompt and reduce the chance of wrong-tool calls in ambiguous prompts. Wire the maximum set only when you genuinely need it.
System prompt guidance
Wiring the tools makes them available; the system prompt is how you steer the agent toward using them well. A few patterns that hold up in practice:
- Name the tools explicitly in the system prompt. The agent reads tool names from the schema, but reinforcing them in prose ("Use
docx_readto inspect the template before callingdocx_write") materially improves first-attempt tool selection. - State an execution discipline when the workflow has a natural order — e.g. "First read all templates, then produce all outputs." Without it, the agent may interleave reads and writes unhelpfully.
- Tell the agent how documents are referenced — by
artifact_id(within the run) or bys3_key(for inputs supplied by the caller). See Sending Documents to the Agent.
The sample system prompt in sandbox/test-artifacts/create_template_workflow.py is a working starting point.
Validating the wiring
Two cheap checks before sending traffic:
- Workflow create returns 200 — backend validation rejects unknown node types and orphan tool edges, so a successful create means the topology is well-formed.
- First
response.in_progressevent lists your tools — when you fire a Responses API request against the agent, the agent's first turn enumerates its tool list internally; if a tool is missing, the agent simply will not call it. Issue a test prompt that explicitly names the tool ("Calldocx_writewith this content") — if the agent does not invoke it, the edge is missing or the handle names are wrong.
See also
- Artifact Tools (docx / xlsx / pdf) — the node reference for the six tool types: params, the
toolhandle, and the direct-to-deepAgentwiring invariant. - Agent Artifacts — the artifact lifecycle,
ArtifactRefshape, and download endpoint. - Sending Documents to the Agent — how to upload docx/xlsx/pdf inputs and reference them in a prompt.
- Responses API — the streaming surface artifact events ride on.