What "handled" actually looks like
Four services, one of them you can try right now. The rest are real examples of the same underlying promise: something messy or manual, taken off your plate and turned into something clean, monitored, and running on its own.
This one, you don't have to imagine
Every other example on this page is a screenshot. This one isn't: the chat bubble in the bottom-right corner of this exact page is a live, working instance, tuned to Briven AI Studio's own services, pricing, and FAQs. Ask it something.
Click the bubble in the corner, or try one of these:
Review responses that sound like the owner, not a template
Three real response scenarios, spanning a regulated healthcare practice, a restaurant, and a home services company. Each shows how tone shifts by industry while staying calm, specific, and non-defensive. All three run on the same automated monitoring: the moment a new review posts, we're alerted automatically, not whenever someone remembers to check.
From messy export to an auto-updating dashboard
Three live-formula workbooks, from a single sales table to a dashboard cross-referencing four linked tables at once. Every KPI, summary, and chart recalculates the moment new rows are pasted in. Click "Raw Data" on any of them to see the real rows behind the numbers.
Revenue by Region
Monthly Trend ($k)
| Order ID | Date | Region | Channel | Category | Units | Revenue | Returned? |
|---|---|---|---|---|---|---|---|
| CO-10001 | Jul 14, 2026 | Northeast | Marketplace | Footwear | 2 | $197.74 | No |
| CO-10002 | Jun 20, 2026 | Northeast | Website | Outerwear | 1 | $134.35 | No |
| CO-10003 | Feb 7, 2026 | Midwest | Marketplace | Base Layers | 4 | $203.97 | No |
| CO-10004 | Feb 2, 2026 | West | Website | Accessories | 3 | $99.46 | No |
| CO-10005 | Apr 28, 2026 | Northeast | Website | Camp Gear | 3 | $270.63 | No |
| CO-10006 | May 29, 2026 | Midwest | Retail Partner | Accessories | 1 | $36.51 | No |
| CO-10007 | Jul 9, 2026 | Southwest | Website | Footwear | 1 | $95.26 | No |
| CO-10008 | Apr 16, 2026 | Southwest | Marketplace | Outerwear | 4 | $549.43 | No |
| CO-10009 | May 5, 2026 | Northeast | Website | Camp Gear | 1 | $90.37 | No |
| CO-10010 | Apr 4, 2026 | Northeast | Website | Base Layers | 2 | $119.86 | No |
| CO-10011 | Feb 15, 2026 | Southeast | Website | Camp Gear | 4 | $320.86 | No |
| CO-10012 | Jun 26, 2026 | Southwest | Website | Accessories | 4 | $162.35 | No |
Showing 12 of 420 rows from the actual workbook.
Avg Days to Resolve by Category
Ticket Status Mix
| Ticket ID | Property | Unit | Category | Submitted | Status | Days to Resolve | Rating |
|---|---|---|---|---|---|---|---|
| WO-2026-1001 | Cedar Ridge | 2D | Appliance | May 10, 2026 | Open | — | — |
| WO-2026-1002 | Magnolia Court | 3E | Plumbing | Jul 4, 2026 | Closed | 4 | 5 |
| WO-2026-1003 | The Wynwood | 1E | Plumbing | May 29, 2026 | Closed | 3 | 4 |
| WO-2026-1004 | Magnolia Court | 4A | General/Cosmetic | May 6, 2026 | Closed | 6 | 4 |
| WO-2026-1005 | Cedar Ridge | 2A | HVAC | Jul 21, 2026 | Closed | 5 | 5 |
| WO-2026-1006 | Magnolia Court | 1E | Plumbing | Jul 27, 2026 | Closed | 3 | 2 |
| WO-2026-1007 | Cedar Ridge | 3B | Appliance | Jul 29, 2026 | Closed | 3 | 5 |
| WO-2026-1008 | Cedar Ridge | 4C | Appliance | Jun 6, 2026 | Closed | 5 | 5 |
| WO-2026-1009 | Cedar Ridge | 2D | HVAC | Jul 25, 2026 | Closed | 4 | 4 |
| WO-2026-1010 | Cedar Ridge | 3F | Electrical | Jul 3, 2026 | Closed | 1 | 2 |
| WO-2026-1011 | The Wynwood | 1A | Appliance | Jun 9, 2026 | Closed | 5 | 1 |
| WO-2026-1012 | The Wynwood | 3F | Electrical | Jul 25, 2026 | Closed | 3 | 4 |
Showing 12 of 260 rows from the actual workbook.
Location Performance
| Location | Revenue | Labor % |
|---|---|---|
| Uptown | $777,993 | 26.6% |
| Downtown | $639,638 | 28.3% |
| Riverside | $457,366 | 29.2% |
| Harbor Point | $322,967 | 31.2% |
| Westgate | $382,868 | 35.8% |
Revenue Trend, Month over Month
▼ 5.3% May → Jun| Date | Location ID | Net Sales | Covers | Comps |
|---|---|---|---|---|
| Apr 1, 2026 | L1 | $6,381.13 | 213 | $217.80 |
| Apr 1, 2026 | L2 | $4,956.32 | 152 | $131.77 |
| Apr 1, 2026 | L3 | $6,639.49 | 220 | $114.29 |
| Apr 1, 2026 | L4 | $3,849.38 | 128 | $131.95 |
| Apr 1, 2026 | L5 | $2,620.52 | 88 | $32.03 |
| Apr 2, 2026 | L1 | $6,802.40 | 215 | $148.24 |
| Apr 2, 2026 | L2 | $4,030.69 | 117 | $81.01 |
| Apr 2, 2026 | L3 | $6,622.83 | 205 | $116.61 |
| Apr 2, 2026 | L4 | $3,095.60 | 85 | $83.59 |
| Apr 2, 2026 | L5 | $3,283.91 | 89 | $65.24 |
| Apr 3, 2026 | L1 | $8,006.24 | 248 | $161.04 |
| Apr 3, 2026 | L2 | $6,158.04 | 219 | $133.03 |
Showing 12 of the Daily Sales tab. Location IDs join out to Locations, Labor Hours, and Waste Log in the other 3 tabs.
Workflows that keep running after delivery
Three real, importable workflow builds, from a straightforward lead-routing pipeline up to a multi-week booking chain with nested payment logic. All three include tested error handling and a human-review step before anything customer-facing goes out. Open "View raw workflow JSON" to see the actual exported file.
{
"name": "Local Plumbing Service: Lead Capture, CRM & Routing",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "new-lead-form",
"responseMode": "onReceived",
"options": {}
},
"id": "1a2b3c4d-0001-4a1a-9b1a-000000000001",
"name": "New Website Form Submission",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [-40, 260],
"notes": "Fires the instant a visitor submits the quote-request form on example.com. Webhook URL is registered with the site's form tool."
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "1Xk9pQ_STERLING_LEAD_LOG_SHEET_ID",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "Leads",
"mode": "name"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"Date": "={{ $now.toFormat('yyyy-MM-dd HH:mm') }}",
"Name": "={{ $json.body.name }}",
"Phone": "={{ $json.body.phone }}",
"Service Needed": "={{ $json.body.service_type }}",
"Job Value Estimate": "={{ $json.body.estimated_value }}",
"Source": "={{ $json.body.utm_source || 'Direct' }}",
"Status": "New"
}
}
},
"id": "1a2b3c4d-0002-4a1a-9b1a-000000000002",
"name": "Add Row to Lead Tracker (Sheets)",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.4,
"position": [200, 260],
"notes": "System of record for every inbound lead, regardless of value."
},
{
"parameters": {
"conditions": {
"options": { "caseSensitive": true, "leftValue": "", "typeValidation": "loose" },
"conditions": [
{
"id": "cond-1",
"leftValue": "={{ Number($json.body.estimated_value) }}",
"rightValue": 1000,
"operator": { "type": "number", "operation": "gte" }
}
],
"combinator": "and"
}
},
"id": "1a2b3c4d-0003-4a1a-9b1a-000000000003",
"name": "IF Job Value >= $1,000",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [440, 260],
"notes": "High-value jobs (re-pipes, water heater replacements, emergency calls) get routed to the owner directly instead of the standard queue."
},
{
"parameters": {
"select": "channel",
"channelId": { "__rl": true, "value": "#high-value-leads", "mode": "name" },
"text": "=🔥 High-value lead: *{{ $json.body.name }}*: {{ $json.body.service_type }}, est. ${{ $json.body.estimated_value }}. Phone: {{ $json.body.phone }}. Call within 15 min for best close rate.",
"otherOptions": {}
},
"id": "1a2b3c4d-0004-4a1a-9b1a-000000000004",
"name": "Slack Alert: Owner (Urgent)",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [700, 140],
"notes": "Posts to a dedicated Slack channel the owner has muted-except-urgent, so this doesn't get lost in general chatter."
},
{
"parameters": {
"select": "channel",
"channelId": { "__rl": true, "value": "#new-leads", "mode": "name" },
"text": "=New lead: {{ $json.body.name }}: {{ $json.body.service_type }}. Added to the tracker, standard follow-up queue.",
"otherOptions": {}
},
"id": "1a2b3c4d-0005-4a1a-9b1a-000000000005",
"name": "Slack Alert: Standard Queue",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [700, 380],
"notes": "Routine leads still get a lightweight notification, just to the general channel rather than paging the owner."
},
{
"parameters": {
"resource": "message",
"operation": "send",
"toList": [{ "phone": "={{ $json.body.phone }}" }],
"message": "Hi {{ $json.body.name }}, thanks for reaching out to Local Plumbing Service! We received your request and will call you back shortly."
},
"id": "1a2b3c4d-0006-4a1a-9b1a-000000000006",
"name": "Auto-Reply SMS to Customer",
"type": "n8n-nodes-base.twilio",
"typeVersion": 1,
"position": [940, 260],
"notes": "Immediate acknowledgment so the customer isn't left wondering whether the form actually submitted."
},
{
"parameters": {
"conditions": {
"options": { "caseSensitive": true, "leftValue": "", "typeValidation": "loose" },
"conditions": [
{
"id": "err-1",
"leftValue": "={{ $json.body.phone }}",
"rightValue": "",
"operator": { "type": "string", "operation": "empty" }
}
],
"combinator": "or"
}
},
"id": "1a2b3c4d-0007-4a1a-9b1a-000000000007",
"name": "Missing Required Field?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [200, 460],
"notes": "Error-handling branch: catches malformed submissions (e.g. phone field blank) before they silently fail downstream."
},
{
"parameters": {
"select": "channel",
"channelId": { "__rl": true, "value": "#automation-alerts", "mode": "name" },
"text": "=⚠️ Lead form submitted with a missing phone number. Raw payload: {{ JSON.stringify($json.body) }}. Please follow up manually.",
"otherOptions": {}
},
"id": "1a2b3c4d-0008-4a1a-9b1a-000000000008",
"name": "Notify: Malformed Submission",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [440, 460],
"notes": "Fallback path so a bad submission is surfaced to a human instead of vanishing."
}
],
"connections": {
"New Website Form Submission": {
"main": [
[
{ "node": "Add Row to Lead Tracker (Sheets)", "type": "main", "index": 0 },
{ "node": "Missing Required Field?", "type": "main", "index": 0 }
]
]
},
"Add Row to Lead Tracker (Sheets)": {
"main": [[{ "node": "IF Job Value >= $1,000", "type": "main", "index": 0 }]]
},
"IF Job Value >= $1,000": {
"main": [
[{ "node": "Slack Alert: Owner (Urgent)", "type": "main", "index": 0 }],
[{ "node": "Slack Alert: Standard Queue", "type": "main", "index": 0 }]
]
},
"Slack Alert: Owner (Urgent)": {
"main": [[{ "node": "Auto-Reply SMS to Customer", "type": "main", "index": 0 }]]
},
"Slack Alert: Standard Queue": {
"main": [[{ "node": "Auto-Reply SMS to Customer", "type": "main", "index": 0 }]]
},
"Missing Required Field?": {
"main": [
[{ "node": "Notify: Malformed Submission", "type": "main", "index": 0 }],
[]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1",
"saveManualExecutions": true,
"callerPolicy": "workflowsFromSameOwner"
},
"meta": {
"templateCredsSetupCompleted": true,
"portfolioSample": true,
"instructions": "Sample workflow for portfolio purposes. Replace the Google Sheet ID, Slack channel IDs, and Twilio credentials with the client's own before deploying live."
}
}
{
"name": "Retail Company: AI Support Ticket Triage",
"nodes": [
{
"parameters": {
"pollTimes": { "item": [{ "mode": "everyMinute" }] },
"mailbox": "support@example.com",
"postProcessAction": "read",
"options": {}
},
"id": "2b3c4d5e-0001-4b2b-9c2b-000000000001",
"name": "New Support Email",
"type": "n8n-nodes-base.emailReadImap",
"typeVersion": 2,
"position": [-40, 260],
"notes": "Polls the shared support inbox every minute. Most email providers don't offer a true webhook trigger, so polling is the realistic option here."
},
{
"parameters": {
"modelId": { "__rl": true, "value": "claude-sonnet-4-6", "mode": "list" },
"messages": {
"values": [
{
"role": "system",
"content": "You are a support-ticket classifier for an outdoor apparel retailer. Read the email and return strict JSON only: {\"category\": one of [\"Order Status\",\"Return/Exchange\",\"Product Question\",\"Complaint\",\"Other\"], \"urgency\": one of [\"Low\",\"Normal\",\"High\"], \"draft_reply\": a short, warm first-pass reply under 80 words}. Do not invent order details you were not given."
},
{
"role": "user",
"content": "=Subject: {{ $json.subject }}\n\nFrom: {{ $json.from }}\n\nBody:\n{{ $json.textPlain }}"
}
]
},
"options": { "temperature": 0.2 }
},
"id": "2b3c4d5e-0002-4b2b-9c2b-000000000002",
"name": "Classify & Draft Reply (Claude)",
"type": "n8n-nodes-base.anthropic",
"typeVersion": 1,
"position": [200, 260],
"notes": "The AI-enhanced step: classifies topic + urgency and drafts a first-pass reply for a human to review, rather than auto-sending anything."
},
{
"parameters": {
"jsCode": "const raw = $input.first().json.content || $input.first().json.text;\nlet parsed;\ntry {\n parsed = JSON.parse(raw);\n} catch (e) {\n parsed = { category: 'Other', urgency: 'Normal', draft_reply: '' };\n}\nreturn [{ json: { ...$input.first().json, ...parsed } }];"
},
"id": "2b3c4d5e-0003-4b2b-9c2b-000000000003",
"name": "Parse Classification JSON",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [440, 260],
"notes": "Safely parses the model's JSON output; falls back to a safe default instead of breaking the workflow if the model ever returns malformed JSON."
},
{
"parameters": {
"conditions": {
"options": { "caseSensitive": true, "leftValue": "", "typeValidation": "loose" },
"conditions": [
{
"id": "urgent-1",
"leftValue": "={{ $json.urgency }}",
"rightValue": "High",
"operator": { "type": "string", "operation": "equals" }
},
{
"id": "complaint-1",
"leftValue": "={{ $json.category }}",
"rightValue": "Complaint",
"operator": { "type": "string", "operation": "equals" }
}
],
"combinator": "or"
}
},
"id": "2b3c4d5e-0004-4b2b-9c2b-000000000004",
"name": "IF High Urgency or Complaint",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [680, 260],
"notes": "Complaints and high-urgency tickets never get auto-routed to the standard queue: they always page a human directly."
},
{
"parameters": {
"select": "channel",
"channelId": { "__rl": true, "value": "#support-urgent", "mode": "name" },
"text": "=🚨 {{ $json.category }} ({{ $json.urgency }}) from {{ $json.from }}\nSubject: {{ $json.subject }}\n\nDraft reply for review:\n{{ $json.draft_reply }}",
"otherOptions": {}
},
"id": "2b3c4d5e-0005-4b2b-9c2b-000000000005",
"name": "Slack: Urgent Review Queue",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [940, 140],
"notes": "A human reviews and edits the AI draft before anything is sent: this workflow never auto-sends a reply to a customer."
},
{
"parameters": {
"operation": "append",
"documentId": { "__rl": true, "value": "1Yk8pR_COASTAL_TICKET_LOG_SHEET_ID", "mode": "id" },
"sheetName": { "__rl": true, "value": "Ticket Log", "mode": "name" },
"columns": {
"mappingMode": "defineBelow",
"value": {
"Date": "={{ $now.toFormat('yyyy-MM-dd HH:mm') }}",
"From": "={{ $json.from }}",
"Subject": "={{ $json.subject }}",
"Category": "={{ $json.category }}",
"Urgency": "={{ $json.urgency }}",
"Draft Reply": "={{ $json.draft_reply }}",
"Status": "Pending Review"
}
}
},
"id": "2b3c4d5e-0006-4b2b-9c2b-000000000006",
"name": "Log to Standard Queue (Sheets)",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.4,
"position": [940, 380],
"notes": "Normal-priority tickets skip the Slack page and go straight into a reviewable queue the support rep checks a few times a day."
},
{
"parameters": {
"conditions": {
"options": { "caseSensitive": true, "leftValue": "", "typeValidation": "loose" },
"conditions": [
{
"id": "empty-body",
"leftValue": "={{ $json.textPlain }}",
"rightValue": "",
"operator": { "type": "string", "operation": "empty" }
}
],
"combinator": "or"
}
},
"id": "2b3c4d5e-0007-4b2b-9c2b-000000000007",
"name": "Empty/Unparseable Email?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [-40, 460],
"notes": "Error-handling branch for emails with no readable body (e.g. image-only or malformed encoding)."
},
{
"parameters": {
"select": "channel",
"channelId": { "__rl": true, "value": "#automation-alerts", "mode": "name" },
"text": "=⚠️ Received a support email that couldn't be auto-classified (empty body). From: {{ $json.from }}, Subject: {{ $json.subject }}. Please check manually.",
"otherOptions": {}
},
"id": "2b3c4d5e-0008-4b2b-9c2b-000000000008",
"name": "Notify: Manual Review Needed",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [200, 460],
"notes": "Fallback so an unparseable email is surfaced to a human rather than silently dropped."
}
],
"connections": {
"New Support Email": {
"main": [
[
{ "node": "Classify & Draft Reply (Claude)", "type": "main", "index": 0 },
{ "node": "Empty/Unparseable Email?", "type": "main", "index": 0 }
]
]
},
"Classify & Draft Reply (Claude)": {
"main": [[{ "node": "Parse Classification JSON", "type": "main", "index": 0 }]]
},
"Parse Classification JSON": {
"main": [[{ "node": "IF High Urgency or Complaint", "type": "main", "index": 0 }]]
},
"IF High Urgency or Complaint": {
"main": [
[{ "node": "Slack: Urgent Review Queue", "type": "main", "index": 0 }],
[{ "node": "Log to Standard Queue (Sheets)", "type": "main", "index": 0 }]
]
},
"Empty/Unparseable Email?": {
"main": [
[{ "node": "Notify: Manual Review Needed", "type": "main", "index": 0 }],
[]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1",
"saveManualExecutions": true,
"callerPolicy": "workflowsFromSameOwner"
},
"meta": {
"templateCredsSetupCompleted": true,
"portfolioSample": true,
"instructions": "Sample workflow for portfolio purposes. Replace the mailbox, Google Sheet ID, Slack channel IDs, and AI credentials with the client's own before deploying live. This workflow only ever drafts replies for human review: it never auto-sends to a customer."
}
}
{
"name": "Photography Studio: Booking to Deposit to Confirmation Chain",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "studio-new-booking",
"responseMode": "onReceived",
"options": {}
},
"id": "3c4d5e6f-0001-4c3c-9d3c-000000000001",
"name": "New Booking Submitted",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [-60, 400],
"notes": "Fires the instant a client submits the booking form on example.com (session type, date, package price)."
},
{
"parameters": {
"resource": "contact",
"operation": "upsert",
"email": "={{ $json.body.email }}",
"additionalFields": {
"firstName": "={{ $json.body.first_name }}",
"lastName": "={{ $json.body.last_name }}",
"phone": "={{ $json.body.phone }}"
}
},
"id": "3c4d5e6f-0002-4c3c-9d3c-000000000002",
"name": "Create/Update Contact (CRM)",
"type": "n8n-nodes-base.hubspot",
"typeVersion": 2,
"position": [200, 400],
"notes": "Creates the client record if new, or updates it if they have booked before. Every booking is tracked as a deal in the CRM pipeline."
},
{
"parameters": {
"resource": "deal",
"operation": "create",
"dealName": "={{ $json.body.first_name }} {{ $json.body.last_name }}, {{ $json.body.session_type }}",
"additionalFields": {
"amount": "={{ $json.body.package_price }}",
"dealStage": "booking_deposit_pending",
"closeDate": "={{ $json.body.session_date }}"
}
},
"id": "3c4d5e6f-0003-4c3c-9d3c-000000000003",
"name": "Create Deal (Deposit Pending)",
"type": "n8n-nodes-base.hubspot",
"typeVersion": 2,
"position": [440, 400],
"notes": "Deal stage doubles as the studio's at-a-glance status tracker across every open booking."
},
{
"parameters": {
"resource": "invoice",
"operation": "create",
"customer": "={{ $json.body.email }}",
"amount": "={{ Math.round($json.body.package_price * 0.3 * 100) / 100 }}",
"description": "=30% deposit, {{ $json.body.session_type }} session on {{ $json.body.session_date }}",
"dueDate": "={{ $now.plus({days: 5}).toISODate() }}"
},
"id": "3c4d5e6f-0004-4c3c-9d3c-000000000004",
"name": "Create 30% Deposit Invoice",
"type": "n8n-nodes-base.stripe",
"typeVersion": 1,
"position": [680, 400],
"notes": "Deposit is always 30% of the package price, calculated at send time rather than hardcoded per package."
},
{
"parameters": {
"sendTo": "={{ $json.body.email }}",
"subject": "=Your studio booking, deposit invoice enclosed",
"message": "=Hi {{ $json.body.first_name }}, thanks for booking your {{ $json.body.session_type }} session! To lock in {{ $json.body.session_date }}, please pay the attached deposit invoice within 5 days. We'll follow up automatically as the date approaches."
},
"id": "3c4d5e6f-0005-4c3c-9d3c-000000000005",
"name": "Send Invoice Email",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [920, 400],
"notes": "Confirms receipt of the booking immediately, before any payment status is known."
},
{
"parameters": {
"amount": 5,
"unit": "days"
},
"id": "3c4d5e6f-0006-4c3c-9d3c-000000000006",
"name": "Wait Until Invoice Due",
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [1160, 400],
"notes": "Pauses the workflow execution itself, no separate scheduled workflow needed to check back in."
},
{
"parameters": {
"resource": "invoice",
"operation": "get",
"invoiceId": "={{ $('Create 30% Deposit Invoice').item.json.id }}"
},
"id": "3c4d5e6f-0007-4c3c-9d3c-000000000007",
"name": "Check Invoice Payment Status",
"type": "n8n-nodes-base.stripe",
"typeVersion": 1,
"position": [1400, 400],
"notes": "Reads the live invoice status directly from Stripe rather than trusting a local flag."
},
{
"parameters": {
"conditions": {
"options": { "caseSensitive": true, "leftValue": "", "typeValidation": "loose" },
"conditions": [
{
"id": "paid-1",
"leftValue": "={{ $json.status }}",
"rightValue": "paid",
"operator": { "type": "string", "operation": "equals" }
}
],
"combinator": "and"
}
},
"id": "3c4d5e6f-0008-4c3c-9d3c-000000000008",
"name": "IF Invoice Paid",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [1640, 400],
"notes": "Splits the workflow into a confirmed path and a reminder/escalation path."
},
{
"parameters": {
"calendar": "={{ 'studio-bookings@example.com' }}",
"start": "={{ $('New Booking Submitted').item.json.body.session_date }}",
"summary": "=Session, {{ $('New Booking Submitted').item.json.body.first_name }} {{ $('New Booking Submitted').item.json.body.last_name }}"
},
"id": "3c4d5e6f-0009-4c3c-9d3c-000000000009",
"name": "Confirm Session on Calendar",
"type": "n8n-nodes-base.googleCalendar",
"typeVersion": 1.3,
"position": [1900, 220],
"notes": "Only ever added to the studio calendar once the deposit clears, so the calendar never shows unconfirmed holds as real bookings."
},
{
"parameters": {
"sendTo": "={{ $('New Booking Submitted').item.json.body.email }}",
"subject": "You're confirmed! Session details enclosed",
"message": "Deposit received, your session is officially on the calendar. We'll send a prep guide 48 hours before your shoot."
},
"id": "3c4d5e6f-0010-4c3c-9d3c-00000000000a",
"name": "Send Confirmation Email",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [2140, 220],
"notes": "Distinct from the initial booking email, confirms the deposit specifically."
},
{
"parameters": {
"select": "channel",
"channelId": { "__rl": true, "value": "#studio-bookings", "mode": "name" },
"text": "=Session confirmed: {{ $('New Booking Submitted').item.json.body.first_name }} {{ $('New Booking Submitted').item.json.body.last_name }}, {{ $('New Booking Submitted').item.json.body.session_type }} on {{ $('New Booking Submitted').item.json.body.session_date }}.",
"otherOptions": {}
},
"id": "3c4d5e6f-0011-4c3c-9d3c-00000000000b",
"name": "Notify Studio Team",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [2380, 220],
"notes": "Keeps the whole team aware of new confirmed sessions without anyone needing to check the CRM manually."
},
{
"parameters": {
"sendTo": "={{ $('New Booking Submitted').item.json.body.email }}",
"subject": "Friendly reminder: deposit still pending",
"message": "=Hi {{ $('New Booking Submitted').item.json.body.first_name }}, just a reminder that your deposit invoice for {{ $('New Booking Submitted').item.json.body.session_date }} is still outstanding. Let us know if you'd like a different payment method or have any questions."
},
"id": "3c4d5e6f-0012-4c3c-9d3c-00000000000c",
"name": "Send Payment Reminder Email",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [1900, 580],
"notes": "First reminder, kept friendly and low-pressure rather than an automatic penalty notice."
},
{
"parameters": {
"amount": 48,
"unit": "hours"
},
"id": "3c4d5e6f-0013-4c3c-9d3c-00000000000d",
"name": "Wait 48 Hours",
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [2140, 580],
"notes": "Gives the reminder email time to land before checking payment status again."
},
{
"parameters": {
"resource": "invoice",
"operation": "get",
"invoiceId": "={{ $('Create 30% Deposit Invoice').item.json.id }}"
},
"id": "3c4d5e6f-0014-4c3c-9d3c-00000000000e",
"name": "Re-check Payment Status",
"type": "n8n-nodes-base.stripe",
"typeVersion": 1,
"position": [2380, 580],
"notes": "Second live status check rather than assuming the reminder worked."
},
{
"parameters": {
"conditions": {
"options": { "caseSensitive": true, "leftValue": "", "typeValidation": "loose" },
"conditions": [
{
"id": "still-unpaid-1",
"leftValue": "={{ $json.status }}",
"rightValue": "paid",
"operator": { "type": "string", "operation": "notEquals" }
}
],
"combinator": "and"
}
},
"id": "3c4d5e6f-0015-4c3c-9d3c-00000000000f",
"name": "IF Still Unpaid",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [2620, 580],
"notes": "Second branch point. A late payment here still reconnects to the confirmed path rather than staying stuck."
},
{
"parameters": {
"select": "channel",
"channelId": { "__rl": true, "value": "#studio-bookings-urgent", "mode": "name" },
"text": "=Deposit still unpaid after reminder: {{ $('New Booking Submitted').item.json.body.first_name }} {{ $('New Booking Submitted').item.json.body.last_name }}, session date {{ $('New Booking Submitted').item.json.body.session_date }}. Recommend a personal follow-up.",
"otherOptions": {}
},
"id": "3c4d5e6f-0016-4c3c-9d3c-000000000010",
"name": "Escalate to Owner",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [2860, 460],
"notes": "Escalations go to a separate, higher-attention channel than routine booking confirmations."
},
{
"parameters": {
"resource": "deal",
"operation": "update",
"dealId": "={{ $('Create Deal (Deposit Pending)').item.json.id }}",
"additionalFields": {
"dealStage": "at_risk_unpaid_deposit"
}
},
"id": "3c4d5e6f-0017-4c3c-9d3c-000000000011",
"name": "Flag Deal At-Risk in CRM",
"type": "n8n-nodes-base.hubspot",
"typeVersion": 2,
"position": [2860, 640],
"notes": "So the studio's pipeline view reflects reality without anyone updating it by hand."
},
{
"parameters": {
"conditions": {
"options": { "caseSensitive": true, "leftValue": "", "typeValidation": "loose" },
"conditions": [
{
"id": "missing-1",
"leftValue": "={{ $json.body.session_date }}",
"rightValue": "",
"operator": { "type": "string", "operation": "empty" }
},
{
"id": "missing-2",
"leftValue": "={{ $json.body.email }}",
"rightValue": "",
"operator": { "type": "string", "operation": "empty" }
}
],
"combinator": "or"
}
},
"id": "3c4d5e6f-0018-4c3c-9d3c-000000000012",
"name": "Missing Required Booking Field?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [200, 620],
"notes": "Error-handling branch, catches a malformed booking submission before it creates a broken CRM record or invoice."
},
{
"parameters": {
"select": "channel",
"channelId": { "__rl": true, "value": "#automation-alerts", "mode": "name" },
"text": "=Booking form submitted with a missing session date or email. Raw payload: {{ JSON.stringify($json.body) }}. Please follow up manually.",
"otherOptions": {}
},
"id": "3c4d5e6f-0019-4c3c-9d3c-000000000013",
"name": "Notify - Malformed Booking",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [440, 620],
"notes": "Fallback so a bad submission is surfaced to a human instead of silently creating a broken deal or invoice."
}
],
"connections": {
"New Booking Submitted": {
"main": [
[
{ "node": "Create/Update Contact (CRM)", "type": "main", "index": 0 },
{ "node": "Missing Required Booking Field?", "type": "main", "index": 0 }
]
]
},
"Create/Update Contact (CRM)": {
"main": [[{ "node": "Create Deal (Deposit Pending)", "type": "main", "index": 0 }]]
},
"Create Deal (Deposit Pending)": {
"main": [[{ "node": "Create 30% Deposit Invoice", "type": "main", "index": 0 }]]
},
"Create 30% Deposit Invoice": {
"main": [[{ "node": "Send Invoice Email", "type": "main", "index": 0 }]]
},
"Send Invoice Email": {
"main": [[{ "node": "Wait Until Invoice Due", "type": "main", "index": 0 }]]
},
"Wait Until Invoice Due": {
"main": [[{ "node": "Check Invoice Payment Status", "type": "main", "index": 0 }]]
},
"Check Invoice Payment Status": {
"main": [[{ "node": "IF Invoice Paid", "type": "main", "index": 0 }]]
},
"IF Invoice Paid": {
"main": [
[{ "node": "Confirm Session on Calendar", "type": "main", "index": 0 }],
[{ "node": "Send Payment Reminder Email", "type": "main", "index": 0 }]
]
},
"Confirm Session on Calendar": {
"main": [[{ "node": "Send Confirmation Email", "type": "main", "index": 0 }]]
},
"Send Confirmation Email": {
"main": [[{ "node": "Notify Studio Team", "type": "main", "index": 0 }]]
},
"Send Payment Reminder Email": {
"main": [[{ "node": "Wait 48 Hours", "type": "main", "index": 0 }]]
},
"Wait 48 Hours": {
"main": [[{ "node": "Re-check Payment Status", "type": "main", "index": 0 }]]
},
"Re-check Payment Status": {
"main": [[{ "node": "IF Still Unpaid", "type": "main", "index": 0 }]]
},
"IF Still Unpaid": {
"main": [
[
{ "node": "Escalate to Owner", "type": "main", "index": 0 },
{ "node": "Flag Deal At-Risk in CRM", "type": "main", "index": 0 }
],
[{ "node": "Confirm Session on Calendar", "type": "main", "index": 0 }]
]
},
"Missing Required Booking Field?": {
"main": [
[{ "node": "Notify - Malformed Booking", "type": "main", "index": 0 }],
[]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1",
"saveManualExecutions": true,
"callerPolicy": "workflowsFromSameOwner"
},
"meta": {
"templateCredsSetupCompleted": true,
"portfolioSample": true,
"instructions": "Sample workflow for portfolio purposes. Replace the HubSpot, Stripe, Gmail, Google Calendar, and Slack credentials with the client's own before deploying live. The late-payment branch reconnects into the same confirmed-session path rather than dead-ending, so a client who pays after the reminder still gets booked normally."
}
}
All businesses shown above are generic placeholders built to demonstrate delivery quality, not real clients.
Want to see this on your own business?
Get in touch and we'll put together something built around what you actually do.
Get in touch