This workflow follows the Emailsend → HTTP Request recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "KHvxUBua7hi5e1x1",
"name": "VenueDesk - Financial Operations (UUID Fix)",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "pay-balance",
"responseMode": "responseNode",
"options": {},
"authentication": "none"
},
"id": "e1f835fd-4c70-475e-acf4-0a378de5e5cc",
"name": "Webhook: Pay Balance",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
0,
0
],
"credentials": {}
},
{
"parameters": {
"jsCode": "// UNWRAP\nconst input = ($input.first().json.body) ? $input.first().json.body : $input.first().json;\n\n// VALIDATE\nconst amount = parseFloat(input.amount);\nif (isNaN(amount) || amount <= 0) throw new Error('Payment amount must be positive');\nif (amount > 100000) throw new Error('Amount exceeds maximum');\nif (!input.booking_id) throw new Error('Missing booking_id');\nif (!input.customer_id) throw new Error('Missing customer_id');\n\n// DETERMINE TYPE \u2014 preserve 'deposit' if caller sends it, otherwise 'balance'\nconst sentType = (input.payment_type || '').toLowerCase();\nconst payment_type = (sentType === 'deposit') ? 'deposit' : 'balance';\n\n// is_partial: only relevant for balance payments\nconst balance = parseFloat(input.balance_due || 999999);\nconst is_partial = payment_type === 'balance' && amount < (balance - 0.005);\n\n// GENERATE REFERENCE NUMBER\nconst ts = new Date();\nconst pad = n => String(n).padStart(2,'0');\nconst stamp = ts.getFullYear() + pad(ts.getMonth()+1) + pad(ts.getDate()) +\n pad(ts.getHours()) + pad(ts.getMinutes()) + pad(ts.getSeconds());\nconst rand = Math.random().toString(36).slice(2,6).toUpperCase();\nconst prefix = payment_type === 'deposit' ? 'DEP' : 'REF';\nconst reference_number = prefix + '-' + stamp + '-' + rand;\n\nreturn {\n json: {\n ...input,\n tenant_id: parseInt(input.tenant_id || $input.first().json.headers?.['x-tenant-id'] || $input.first().json.query?.tenant_id || '0'),\n amount,\n payment_type,\n is_partial,\n reference_number,\n payment_method: input.payment_method || 'cash',\n deposit_reference: input.deposit_reference || null,\n // Explicit trim prevents whitespace-corrupted JWTs reaching the auth header\n jwt: String(input.jwt || '').trim()\n }\n};\n"
},
"id": "6c1b0790-6c06-419b-bf26-956a04753044",
"name": "Code: Sanitize",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
224,
0
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.venuedesk.co.uk/payments/record",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "<redacted-credential>"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"options": {
"response": {
"response": {
"fullResponse": false,
"neverError": true
}
},
"timeout": 15000
},
"sendBody": true,
"contentType": "json",
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ booking_id: $json.booking_id, amount: $json.amount, payment_method: $json.payment_method, payment_type: $json.payment_type || 'balance', reference: $json.reference || '', jwt: $json.jwt || '' }) }}"
},
"id": "http-record-payment-001",
"name": "DB: Record Payment",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"continueOnFail": true,
"position": [
448,
0
]
},
{
"parameters": {
"jsCode": "return $input.all();"
},
"id": "code-passthrough-001",
"name": "DB: Update Balance",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
672,
0
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ status: 'success', message: 'Payment processed', reference_number: $('DB: Record Payment').first().json.reference_number || $('DB: Record Payment').first().json.id, payment_type: $('Code: Sanitize').first().json.is_partial ? 'partial' : 'full', amount: $('Code: Sanitize').first().json.amount}) }}",
"options": {
"responseHeaders": {
"entries": [
{
"name": "Access-Control-Allow-Origin",
"value": "*"
}
]
}
}
},
"id": "ee61c79c-62a2-42d4-8df2-45466f044461",
"name": "Respond: Success",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
880,
0
]
},
{
"parameters": {
"method": "GET",
"url": "=https://api.venuedesk.co.uk/bookings/{{ $('Code: Sanitize').first().json.booking_id }}",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "={{ 'Bearer ' + $('Code: Sanitize').first().json.jwt }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"options": {
"response": {
"response": {
"fullResponse": false,
"neverError": true
}
},
"timeout": 15000
}
},
"id": "http-get-customer-det-001",
"name": "DB: Get Customer Details",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"continueOnFail": true,
"position": [
880,
208
]
},
{
"id": "code-build-email-001",
"name": "Code: Build Email Content",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1120,
208
],
"parameters": {
"jsCode": "const sanitize = $('Code: Sanitize').first().json;\nconst paymentNode = $('DB: Record Payment').first().json;\n\n// paymentData: use API response when successful; fall back to {} so we never\n// crash if DB: Record Payment itself failed (network error, 5xx etc.)\nconst paymentData = paymentNode?.success ? (paymentNode.data ?? paymentNode) : {};\n// detailsRaw: DB: Get Customer Details enrichment (best-effort, may 401)\nconst detailsRaw = ($json.success !== false) ? ($json.data ?? $json) : {};\n// webhookData: original sanitised webhook body as the final safety net\nconst webhookData = sanitize;\n\n// Merge order: webhookData (base) \u2192 paymentData (richer) \u2192 detailsRaw (freshest)\nconst raw = { ...webhookData, ...paymentData, ...detailsRaw };\n\nconst customer = {\n ...raw,\n email: raw.customer_email || raw.email || sanitize.email || '',\n full_name: raw.customer_name || raw.full_name || 'Valued Customer',\n event_date: raw.event_date || raw.date_from || raw.booking_date || null,\n};\n\nconst payment_type = sanitize.payment_type;\nconst is_partial = sanitize.is_partial;\nconst amount = parseFloat(sanitize.amount || 0);\nconst ref = sanitize.reference_number || '\\u2014';\nconst remaining = parseFloat(raw.balance_due ?? raw.new_balance ?? 0);\nconst total = parseFloat(customer.total_amount || 0);\n\nconst fmt = n => '\\u00a3' + parseFloat(n || 0).toFixed(2);\nconst fmtDate = d => {\n if (!d) return '\\u2014';\n const dt = new Date(String(d).slice(0,10) + 'T00:00:00');\n return dt.toLocaleDateString('en-GB', { weekday:'long', day:'numeric', month:'long', year:'numeric' });\n};\nconst fmtTime = t => t ? String(t).slice(0,5) : '\\u2014';\n\nlet subject, headingColour, headingText, statusBadge, bodyText;\n\nif (payment_type === 'deposit') {\n subject = `Deposit Payment Confirmed \\u2014 ${fmtDate(customer.event_date)} | Ref: ${ref}`;\n headingColour = '#6366f1';\n headingText = 'Deposit Payment Confirmed';\n statusBadge = { text:'DEPOSIT RECEIVED', bg:'#eef2ff', color:'#4f46e5' };\n bodyText = `Thank you for your deposit payment of <strong>${fmt(amount)}</strong>. Your booking for <strong>${customer.room_name || 'your venue'}</strong> is now secured.`;\n} else if (is_partial) {\n subject = `Payment Received \\u2014 Remaining Balance: ${fmt(remaining)} | Ref: ${ref}`;\n headingColour = '#f59e0b';\n headingText = 'Partial Balance Payment Received';\n statusBadge = { text:'PAYMENT RECEIVED', bg:'#fffbeb', color:'#b45309' };\n bodyText = `Thank you for your payment of <strong>${fmt(amount)}</strong>. Your remaining balance of <strong>${fmt(remaining)}</strong> is due before your event date.`;\n} else {\n subject = `Balance Fully Settled \\u2014 Booking Confirmed \\u2713 | Ref: ${ref}`;\n headingColour = '#10b981';\n headingText = 'Balance Fully Settled \\u2713';\n statusBadge = { text:'FULLY PAID', bg:'#ecfdf5', color:'#065f46' };\n bodyText = `Thank you for your final payment of <strong>${fmt(amount)}</strong>. Your booking balance is now <strong>fully settled</strong> \\u2014 we look forward to hosting your event!`;\n}\n\nconst remainingRow = remaining > 0\n ? `<tr style=\"border-top:1px solid #e2e8f0;\"><td style=\"padding:8px 0 5px;color:#64748b;\">Balance Remaining</td><td style=\"padding:8px 0 5px;color:#f59e0b;font-weight:700;text-align:right;\">${fmt(remaining)}</td></tr>`\n : `<tr style=\"border-top:1px solid #e2e8f0;\"><td style=\"padding:8px 0 5px;color:#64748b;\">Balance Remaining</td><td style=\"padding:8px 0 5px;color:#10b981;font-weight:700;text-align:right;\">\\u00a30.00 \\u2014 Fully Paid \\u2713</td></tr>`;\n\nconst html = `<!DOCTYPE html>\n<html><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\"></head>\n<body style=\"margin:0;padding:0;background:#f8fafc;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;\">\n <div style=\"max-width:600px;margin:0 auto;padding:32px 16px;\">\n <div style=\"background:${headingColour};border-radius:12px 12px 0 0;padding:32px;text-align:center;\">\n <p style=\"margin:0 0 6px;color:rgba(255,255,255,0.8);font-size:12px;letter-spacing:1.5px;text-transform:uppercase;\">VenueDesk Booking System</p>\n <h1 style=\"margin:0;color:#ffffff;font-size:22px;font-weight:700;line-height:1.3;\">${headingText}</h1>\n </div>\n <div style=\"background:#ffffff;border:1px solid #e2e8f0;border-top:none;border-radius:0 0 12px 12px;padding:32px;\">\n <div style=\"text-align:center;margin-bottom:24px;\">\n <span style=\"display:inline-block;background:${statusBadge.bg};color:${statusBadge.color};font-size:11px;font-weight:700;letter-spacing:1.5px;padding:6px 18px;border-radius:20px;\">${statusBadge.text}</span>\n </div>\n <p style=\"margin:0 0 16px;color:#1e293b;font-size:15px;\">Dear <strong>${customer.full_name || 'Valued Customer'}</strong>,</p>\n <p style=\"margin:0 0 24px;color:#374151;font-size:15px;line-height:1.7;\">${bodyText}</p>\n <div style=\"background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:20px;margin-bottom:16px;\">\n <p style=\"margin:0 0 14px;color:#0f172a;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:0.8px;\">Booking Details</p>\n <table style=\"width:100%;border-collapse:collapse;font-size:14px;\">\n <tr><td style=\"padding:5px 0;color:#64748b;width:45%;\">Room / Venue</td><td style=\"padding:5px 0;color:#1e293b;font-weight:600;\">${customer.room_name || '\\u2014'}</td></tr>\n <tr><td style=\"padding:5px 0;color:#64748b;\">Event Date</td><td style=\"padding:5px 0;color:#1e293b;font-weight:600;\">${fmtDate(customer.event_date)}</td></tr>\n <tr><td style=\"padding:5px 0;color:#64748b;\">Time</td><td style=\"padding:5px 0;color:#1e293b;font-weight:600;\">${fmtTime(customer.start_time)} \\u2013 ${fmtTime(customer.end_time)}</td></tr>\n </table>\n </div>\n <div style=\"background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:20px;margin-bottom:24px;\">\n <p style=\"margin:0 0 14px;color:#0f172a;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:0.8px;\">Payment Summary</p>\n <table style=\"width:100%;border-collapse:collapse;font-size:14px;\">\n <tr><td style=\"padding:5px 0;color:#64748b;\">Total Booking Value</td><td style=\"padding:5px 0;color:#1e293b;text-align:right;\">${fmt(total)}</td></tr>\n <tr><td style=\"padding:5px 0;color:#64748b;\">Amount Paid (This Transaction)</td><td style=\"padding:5px 0;color:${headingColour};font-weight:700;text-align:right;\">${fmt(amount)}</td></tr>\n ${remainingRow}\n </table>\n </div>\n <div style=\"background:#eef2ff;border-radius:8px;padding:14px 20px;margin-bottom:28px;text-align:center;\">\n <p style=\"margin:0;color:#64748b;font-size:12px;\">Payment Reference</p>\n <p style=\"margin:4px 0 0;color:#4f46e5;font-size:17px;font-weight:700;letter-spacing:0.5px;\">${ref}</p>\n </div>\n <p style=\"margin:0;color:#64748b;font-size:13px;line-height:1.6;\">Please keep this email as your payment receipt. If you have any questions, reply quoting your reference number or contact us at <a href=\"mailto:bookings@venuedesk.co.uk\" style=\"color:#6366f1;text-decoration:none;\">bookings@venuedesk.co.uk</a>.</p>\n </div>\n <div style=\"text-align:center;padding:20px 0 0;\">\n <p style=\"margin:0;color:#94a3b8;font-size:11px;\">This is an automated message from VenueDesk Booking System. Please do not reply to this email directly.</p>\n </div>\n </div>\n</body></html>`;\n\nreturn { json: { ...customer, subject, html, email: customer.email } };"
}
},
{
"id": "email-pay-conf-001",
"name": "Email: Payment Confirmation",
"type": "n8n-nodes-base.emailSend",
"typeVersion": 2.1,
"position": [
1344,
208
],
"credentials": {
"smtp": {
"name": "<your credential>"
}
},
"parameters": {
"fromEmail": "bookings@venuedesk.co.uk",
"toEmail": "={{ $json.email }}",
"subject": "={{ $json.subject }}",
"html": "={{ $json.html }}",
"options": {
"replyTo": "bookings@venuedesk.co.uk"
}
}
},
{
"id": "if-fully-paid-001",
"name": "If Fully Paid?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1568,
208
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "check_bal",
"leftValue": "={{ parseFloat($('DB: Update Balance').first().json.balance_due) }}",
"rightValue": 0,
"operator": {
"type": "number",
"operation": "lte"
}
}
],
"combinator": "and"
},
"options": {}
}
}
],
"connections": {
"Webhook: Pay Balance": {
"main": [
[
{
"node": "Code: Sanitize",
"type": "main",
"index": 0
}
]
]
},
"Code: Sanitize": {
"main": [
[
{
"node": "DB: Record Payment",
"type": "main",
"index": 0
}
]
]
},
"DB: Record Payment": {
"main": [
[
{
"node": "DB: Update Balance",
"type": "main",
"index": 0
},
{
"node": "Respond: Success",
"type": "main",
"index": 0
}
]
]
},
"DB: Update Balance": {
"main": [
[
{
"node": "DB: Get Customer Details",
"type": "main",
"index": 0
}
]
]
},
"DB: Get Customer Details": {
"main": [
[
{
"node": "Code: Build Email Content",
"type": "main",
"index": 0
}
]
]
},
"Code: Build Email Content": {
"main": [
[
{
"node": "Email: Payment Confirmation",
"type": "main",
"index": 0
}
]
]
},
"Email: Payment Confirmation": {
"main": [
[
{
"node": "If Fully Paid?",
"type": "main",
"index": 0
}
]
]
},
"If Fully Paid?": {
"main": [
[]
]
}
},
"settings": {
"executionOrder": "v1",
"binaryMode": "separate",
"availableInMCP": true,
"callerPolicy": "workflowsFromSameOwner"
},
"staticData": null,
"meta": null,
"versionId": "a54f2490-8b93-4527-861f-d6c5ba715c28"
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
smtp
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
VenueDesk - Financial Operations (UUID Fix). Uses httpRequest, emailSend. Webhook trigger; 9 nodes.
Source: https://github.com/AndyJay72/VenueDesk/blob/main/n8n-workflows/KHvxUBua7hi5e1x1_clean.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
세미나 데모 용 워크플로우. Uses httpRequest, emailSend. Webhook trigger; 17 nodes.
VenueDesk - Cancel Booking (Series Support). Uses emailSend, httpRequest. Webhook trigger; 17 nodes.
worklow_doc. Uses httpRequest, readBinaryFile, n8n-nodes-docxtemplater, emailSend. Webhook trigger; 15 nodes.
WF2 - Upload Manual | JurisAI. Uses httpRequest, emailSend. Webhook trigger; 15 nodes.
Deliver personalized files instantly after PayPal transactions using n8n – without writing a single backend line.