The n8n QR Code node lets you generate QR codes programmatically inside any workflow — no external API key, no third-party service, no per-scan fee. Drop in a URL, a vCard string, or any plain text and the node outputs a PNG binary you can email, upload to S3, embed in a PDF, or write to disk.
This guide covers every option the node exposes, the gotchas that trip people up, three production-ready patterns, and a free workflow JSON you can import today.
What the QR Code node does
The node has a single operation: Generate. You give it a string and it returns a binary PNG file. That's it — deliberately minimal, zero credentials required, and fast enough to use in real-time webhook flows.
| Setting | Options | Notes |
|---|---|---|
| Text | Any string (URL, text, vCard…) | Expression-supported — reference upstream data |
| Width | Integer (px) | Default 200. 300–600 for print; 150 for email thumbnails |
| Margin | Integer (quiet zone) | Default 4. QR spec requires ≥ 4; scanners may fail below that |
| Type | png |
Only PNG is supported |
| Error Correction Level | L / M / Q / H | Higher = survives more damage; also makes the QR denser |
| Output binary property | Field name | Default data. Change if you're chaining to another binary node |
Error correction levels
| Level | Recovery capacity | Use when |
|---|---|---|
| L (Low) | ~7% | Clean digital environments (email, screen) |
| M (Medium) | ~15% | Most print use cases — good default |
| Q (Quartile) | ~25% | Outdoor signage, slightly dirty surfaces |
| H (High) | ~30% | Logos overlaid on the QR, damaged/worn labels |
Common gotchas
1. The output is a binary item — not a base64 string
The node writes to the binary property (default data). If a downstream node expects a string URL, use a Code node or $binary.data.data to extract the base64 representation. For most cases (email attachment, S3 upload, Write to Disk) the binary output works directly.
2. Margin below 4 breaks some scanners
The QR specification mandates a quiet zone of at least 4 modules. Aggressive cropping (margin 0–2) causes iOS Camera and many enterprise scanners to fail. Keep margin ≥ 4 unless you're adding your own padding in a downstream image-processing step.
3. Width is the total image width, not the module size
A 200 px image with a 40-character URL will have tiny modules. If you're generating codes destined for print, use 600–1000 px and let the printer scale down — never scale up a small QR code.
4. Very long strings reduce scan reliability
QR codes encode the string literally. A 500-character JSON payload technically fits but produces a dense grid that cheap scanners fail on. For data payloads, encode a short lookup key and resolve on the back-end.
5. Chaining with Convert to File / Move Binary Data
If you rename the binary property or use Move Binary Data, make sure the downstream node references the new property name — not the default data.
Three production patterns
Pattern 1 — Ticket QR code emailer
A customer purchases a ticket via a Stripe webhook. Generate a unique QR code embedding the order ID, attach it to a confirmation email, and upload a copy to Google Drive.
Webhook (Stripe checkout.session.completed)
→ Set node (extract customer email, order ID, event name)
→ QR Code node (text: {{ $json.orderId }}, width: 400, error correction: M)
→ Gmail node (attach $binary.data as ticket-qr.png)
→ Google Drive node (upload binary → /tickets/{{ $json.orderId }}.png)
Free workflow JSON below — swap in your Stripe webhook URL and Gmail credential.
Pattern 2 — Bulk product label generator
A Google Sheets row-per-product feed triggers the workflow. For each product, generate a QR code pointing to https://yoursite.com/products/{SKU}, then write the PNG to a local folder (self-hosted) or upload to S3.
Schedule Trigger (daily 02:00)
→ Google Sheets (read all rows from "Products" sheet)
→ QR Code node (text: https://yoursite.com/products/{{ $json.SKU }}, width: 600, margin: 6)
→ Write Binary File / S3 node (filename: {{ $json.SKU }}.png)
Set error correction to Q or H if labels go on physical packaging — print processes and warehouse environments are rough on quiet zones.
Pattern 3 — vCard QR code for business cards
Accept a webhook POST with contact fields, generate a vCard-formatted QR code, and return it as a binary response. Useful for conference badge printing flows or "share my contact" mini-apps.
Webhook (POST /generate-vcard-qr)
→ Code node (build vCard string from request body)
→ QR Code node (text: {{ $json.vcard }}, width: 500, errorCorrectionLevel: M)
→ Respond to Webhook node (binary response, Content-Type: image/png)
vCard format:
BEGIN:VCARD
VERSION:3.0
FN:Jane Smith
ORG:Acme Corp
TEL:+15550001234
EMAIL:jane@acme.com
URL:https://acme.com
END:VCARD
Free workflow JSON
Import this into n8n (Settings → Import Workflow) to get the ticket QR emailer starter:
{
"name": "QR Code Ticket Emailer (Starter)",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "qr-ticket",
"responseMode": "lastNode"
},
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [240, 300]
},
{
"parameters": {
"assignments": {
"assignments": [
{ "name": "orderId", "value": "={{ $json.body.orderId }}", "type": "string" },
{ "name": "customerEmail", "value": "={{ $json.body.email }}", "type": "string" },
{ "name": "eventName", "value": "={{ $json.body.eventName }}", "type": "string" }
]
}
},
"name": "Set Fields",
"type": "n8n-nodes-base.set",
"typeVersion": 3,
"position": [460, 300]
},
{
"parameters": {
"text": "={{ $json.orderId }}",
"width": 400,
"margin": 4,
"errorCorrectionLevel": "M"
},
"name": "QR Code",
"type": "n8n-nodes-base.qrCode",
"typeVersion": 1,
"position": [680, 300]
},
{
"parameters": {
"respondWith": "binary",
"options": {}
},
"name": "Respond to Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [900, 300]
}
],
"connections": {
"Webhook": { "main": [[{ "node": "Set Fields", "type": "main", "index": 0 }]] },
"Set Fields": { "main": [[{ "node": "QR Code", "type": "main", "index": 0 }]] },
"QR Code": { "main": [[{ "node": "Respond to Webhook", "type": "main", "index": 0 }]] }
}
}
Quick reference
| Goal | Setting |
|---|---|
| High-resolution print label | Width 800+, margin 6, error correction Q |
| Email thumbnail | Width 150–200, margin 4, error correction L |
| Logo overlay | Error correction H (30% recovery) |
| Outdoor signage | Error correction Q or H, width 600+ |
| Real-time webhook response | Default settings (fast, small) |
| Chain to Gmail attachment | Output binary property → attach in Gmail node as-is |
What to build next
The QR Code node pairs naturally with:
- Google Sheets — generate codes in bulk from a sheet of SKUs or URLs
- Respond to Webhook — return a QR PNG in real time from a REST endpoint
- Gmail / Send Email — embed codes as inline images or attachments
- Write Binary File — dump codes to a local folder for batch printing (self-hosted)
- S3 / Google Drive — persist codes for later retrieval
If you want a pre-built workflow that handles the full ticket flow (Stripe → QR → Gmail → Drive), the n8n Workflow Starter Pack includes this pattern and 9 others — grab it for $29.
Questions? Drop them in the comments — what are you generating QR codes for?
Free: n8n Integration Checklist
25 production checks before you ship any n8n workflow — credentials, error handling, dedup, and integration-specific gotchas.
→ Get the free checklist
Top comments (1)
Are you using the QR Code node to generate ticket QR codes, bulk SKU labels, or vCard contacts? Drop your use case below — curious what people are encoding in n8n.