
Description
Use when handling files, images, attachments, or binary data in n8n, OR when an AI agent needs to take a user-uploaded file as tool input or return a generated file. For Data Tables (schemas, dedup, persistent state), see the separate n8n-data-tables-official skill. Triggers on "file", "image", "PDF", "attachment", "binary", "upload", "download", chat trigger with files, agent tool that needs a file, vision/multimodal, or any handling of non-JSON file data.
SKILL.md
n8n Binary and Data
n8n handles two kinds of data: JSON (in $json) and binary (in $binary), flowing side-by-side. Binary has sharp edges around agent tools, storage, and display contexts (chat surfaces, message rendering).
For tabular storage (Data Tables), see the n8n-data-tables-official skill.
Non-negotiables
- Binary is in
$binary, not$json. Don't read file contents from$json. - Binary cannot cross the agent tool boundary in either direction. Tool parameters are JSON only (via
fromAi()), and tool results are JSON only. Pre-stage binary in storage and pass keys/URLs through JSON. The agent'spassthroughBinaryImages: truelets the LLM see uploaded images for vision, but it does NOT enable tools to receive them. Seereferences/AGENT_TOOL_BINARY.md.
Strong defaults
- Merge nodes keep binary in context. When a side computation strips binary, merge it back rather than re-fetching. See
references/MERGE_FOR_CONTEXT.md.
Binary basics
In n8n, each item has two slots:
{
json: { ... }, // your data
binary: { // your files
data: { // 'data' is the typical key; can be any name
data: '<base64>',
mimeType: 'application/pdf',
fileName: 'invoice.pdf',
fileExtension: 'pdf',
},
},
}
$binary.<key> reads the named property. Most file-handling nodes have a binaryPropertyName parameter, the key inside $binary.
Setting binary
File-producing nodes (HTTP Request with binary response, Read Files, etc.) populate $binary automatically. To produce binary in a Code node:
return [{
json: { ... },
binary: {
data: {
data: Buffer.from(content).toString('base64'),
mimeType: 'text/plain',
fileName: 'output.txt',
},
},
}]
Reading binary
// In a Code node
const buffer = await this.helpers.getBinaryDataBuffer(0, 'data')
const text = buffer.toString('utf-8')
Most workflows don't need to read binary directly. Pass it through to consumer nodes (email attachments, file uploads, etc.).
See references/BINARY_BASICS.md for more.
Agent tool gymnastics
Agent tools (sub-workflows wired into a LangChain Agent, or workflows exposed as MCP tools) have a constraint: parameters and results are JSON, not binary. Affects both directions.
Inbound (user uploads → tool consumes): the chat trigger gives files[]. The agent can have passthroughBinaryImages: true for vision, but fromAi() can't pass binary to a tool. So:
- Pre-stage uploaded files: hash a key, upload to private storage.
- Inject the keys into the agent's system prompt: "Files passed in: {originalFileName, fileName}. Use EXACTLY the
fileNamefield when calling tools." - The tool's
fromAi('imageName', '...', 'string')receives the key. The sub-workflow downloads from storage.
Outbound (tool produces → agent returns): a tool generates a file. It can't return raw binary.
- Generate binary internally.
- Upload to storage, get back URL/key.
- Return JSON:
{ ok: true, file_id: '...', url: '...' }. - The agent embeds the URL in its response, or another tool fetches by key.
For the full pattern including the async-via-webhook variant for long-running tools, see references/AGENT_TOOL_BINARY.md.
Merge for keeping binary in context
A JSON-only operation (Edit Fields, Code, IF) often strips binary from the item. To keep it:
[Source with binary] ─┬─→ [Edit Fields: transform JSON] ─┐
│ ├─→ [Merge: by position] ─→ [Email with attachment]
└─────────────────────────────────────┘
Merge combines the streams, and binary survives. See references/MERGE_FOR_CONTEXT.md.
CDN requirement for chat surfaces
When a workflow generates an image and the user wants it embedded in a chat message (Slack, Discord, Teams, Telegram, embedded webhook chat, etc.):
- Binary on the item isn't enough. Chat surfaces don't read
$binary; they render messages that reference images by URL (or via platform-specific file upload APIs). - The image must live somewhere a URL can fetch. Upload to a CDN or object store first.
- The user configures this storage. Not built into n8n.
Common options span object storage (S3, R2, GCS, Azure Blob, Vercel Blob, Supabase Storage) and drive-style services (Dropbox, Google Drive, OneDrive, Box). Ask the user what they use rather than defaulting to S3.
See references/CDN_REQUIREMENT.md.
Data Tables
For Data Tables, see the n8n-data-tables-official skill. Distinct surface with its own gotchas (default columns, no foreign keys, no JSON column type, manual-mapping UI quirk).
Reference files
| File | Read when |
|---|---|
references/BINARY_BASICS.md | First time handling binary, or reading/writing the $binary slot |
references/AGENT_TOOL_BINARY.md | Agent tool needs a user-uploaded file, or produces a file (the boundary in either direction) |
references/MERGE_FOR_CONTEXT.md | Binary disappears after a JSON transform and needs to re-attach |
references/CDN_REQUIREMENT.md | Showing images in a chat surface or other places that need URL-referenced images |
Anti-patterns
| Anti-pattern | What goes wrong | Fix |
|---|---|---|
Trying to read file content from $json | Binary isn't in $json | Use $binary |
| Building an agent tool that returns binary directly | Tool output is JSON-only, so binary doesn't survive | Upload to storage, return key/URL in JSON (see AGENT_TOOL_BINARY.md) |
Trying to pass uploaded chat files into a tool via fromAi | fromAi doesn't carry binary, so the tool gets nothing | Pre-stage uploads to storage, inject keys in the system prompt, and have the tool download by key |
Setting passthroughBinaryImages: true and assuming tools can now see the file | The flag only affects what the LLM sees, not what tools receive | Still need the upload-and-pass-key pattern for tools |
| Losing binary after a JSON transform | The transform's output item doesn't have binary | Use Merge to combine the JSON output with the binary stream |
| Storing image in n8n binary and expecting a chat surface to display | Chat surfaces need URL-accessible images (or a platform-native file upload), not raw $binary | Upload to CDN, embed URL or use the platform's file API |
| Hardcoding binary base64 in a Code node | Massive workflow JSON, slow, leaky | Reference binary via $binary properly, or upload to storage and reference by URL |
More skills from the skills repository
View all 14 skillsn8n-agents-official
build AI agents in n8n
Jul 8AgentsLLMWorkflow Automationn8n-code-nodes-official
write custom logic in n8n
Jul 8EngineeringJavaScriptPythonn8n-credentials-and-security-official
manage credentials and security in n8n
Jul 8AuthenticationAutomationn8nOAuth +1n8n-data-tables-official
manage n8n Data Tables
Jul 8Data ModelingDatabasen8nWorkflow Automationn8n-debugging-official
debug and fix n8n workflow errors
Jul 8AutomationDebuggingn8nWorkflow Automationn8n-error-handling-official
implement error handling in n8n
Jul 8Debuggingn8nOperationsWorkflow Automation
More from n8n
View publisherconfig-evals
configure workflow evaluations
n8n
Jul 17Evalsn8nTestingWorkflow Automationcredential-setup-with-computer-use
configure n8n credentials via browser
n8n
Jun 30AutomationConfigurationn8nOAuthdata-table-manager
manage n8n Data Tables
n8n
Jun 30Data EngineeringDatabasen8ndebugging-executions
debug failed n8n workflow executions
n8n
Jun 30AutomationDebuggingn8nWorkflow Automationintent-recognition
classify automation requests by control flow
n8n
Jul 20AgentsAutomationn8nWorkflow Automationn8n-cli
manage n8n workflows from the CLI
n8n
Apr 6AutomationCLIn8nWorkflow Automation