Turn Cal.com Bookings into Stripe Invoices
Ready to automate?
Browse 5,000+ copy-paste n8n workflow templates.
When a client books time with you, the last thing you want to do is open Stripe and type out an invoice manually. But that is exactly what most freelancers and consultants do. The booking system and the billing system never talk to each other, so someone has to bridge the gap — and that someone is usually you, at the end of a long day.
The result is predictable: invoices sent late, details typed wrong, or clients who booked and never paid because no one followed up in time.
This guide shows you how to close that gap with n8n. When Cal.com confirms a booking, an n8n workflow creates a Stripe customer, builds an invoice, and sends it before you have glanced at your phone.
What You Will Build
By the end of this tutorial you will have a live n8n workflow that:
- Receives booking events from Cal.com via webhook
- Looks up or creates the client in Stripe by email
- Creates a line item and finalizes a Stripe invoice
- Emails the invoice to the client automatically through Stripe
The whole thing runs without your input. You take the booking; Stripe handles the bill.
Prerequisites
You need:
- A Cal.com account (free or paid) with at least one event type configured
- A Stripe account in live or test mode
- An n8n instance — cloud or self-hosted
No paid n8n tier is required for this workflow. The free cloud plan and a self-hosted Community Edition install both work.
Example n8n Workflow
Step 1: Cal.com Webhook Trigger
Start a new workflow and add a Webhook node as the trigger. Set the HTTP method to POST and copy the generated webhook URL.
In Cal.com, go to Settings → Developer → Webhooks and create a new webhook. Paste your n8n webhook URL and subscribe to the BOOKING_CREATED event. Save it.
Back in n8n, switch the Webhook node to listen mode and make a test booking in Cal.com. The full JSON payload will arrive in n8n — it includes the attendee's name, email, event title, start time, and a unique booking reference ID.
Step 2: Parse the Payload with a Set Node
Add a Set node after the trigger. Extract the fields you need:
attendeeNamefrombody.payload.attendees[0].nameattendeeEmailfrombody.payload.attendees[0].emaileventTitlefrombody.payload.titlebookingUidfrombody.payload.uidstartTimefrombody.payload.startTime
The Set node keeps the rest of the workflow readable — downstream nodes reference clean variable names rather than deeply nested JSON paths.
Step 3: Find or Create a Stripe Customer
Add a Stripe node and set the resource to Customer and the operation to Search. Search by email using the attendeeEmail variable.
This avoids creating duplicate Stripe records when the same client books multiple sessions over time.
After the Stripe node, add an IF node. Check whether the search returned any results. If yes, pass the existing customer ID forward. If no, route to a second Stripe node set to Customer → Create, passing attendeeName as the name and attendeeEmail as the email.
Both branches of the IF node merge into the next step using n8n's Merge node with the Pass-Through operation.
Step 4: Create the Invoice and Line Item
Add a Stripe node set to Invoice → Create. Set the customer field to the Stripe customer ID from the previous step. Set collection_method to send_invoice and days_until_due to your preferred payment window (7 or 14 days is common for service work).
Next, add another Stripe node set to Invoice Item → Create. Point it at the same customer and the newly created invoice ID. Configure:
description— use theeventTitlevariable (for example, "60-Minute Strategy Session")amount— your rate in cents; $150.00 =15000currency—usd
To make the amount dynamic, add a Switch node before this step. Map each Cal.com event type slug to a price in cents. A 30-minute intro call and a full-day workshop then bill at their correct rates without any manual intervention.
Step 5: Finalize and Send
Add a Stripe node set to Invoice → Finalize Invoice, passing the invoice ID from Step 4. Finalizing moves the invoice from draft to open and locks the line items.
Add one more Stripe node set to Invoice → Send Invoice. Stripe emails the invoice directly to the client using its own invoice email template. The client receives a hosted payment page where they can pay by card, along with a PDF attachment.
Step 6: Guard Against Duplicate Invoices
Cal.com can fire BOOKING_CREATED more than once if a booking is rescheduled or if a webhook retry occurs. To prevent double-billing, add a deduplication check at the top of the workflow — between the Webhook Trigger and the Set node.
The simplest approach: use a Supabase node (or an HTTP Request node pointed at any key-value store) to look up the bookingUid before processing. If a row with that UID already exists, route to a No Operation node and stop. On success, write the UID, Stripe invoice ID, and timestamp to the same store so future runs can check it.
Key Configuration Notes
Use test mode first. Stripe's test mode lets you trigger real invoice emails and payment flows without charging anyone. Build and verify the full workflow in test mode before switching to live API keys.
Webhook signature verification. Cal.com supports a shared secret on webhooks. In your n8n Webhook node, enable header authentication and validate the X-Cal-Signature-256 header to reject requests that do not originate from Cal.com.
Amount units. Stripe expects monetary amounts in the smallest currency unit — cents for USD. $150 is 15000, not 150.
Auto-advance. Setting auto_advance to true on the Invoice Create step tells Stripe to finalize the invoice automatically after one hour if you have not done so manually. Useful for fully hands-off deployments; skip it if you want to review invoices before they go out.
What to Build Next
Once the core flow runs reliably, these extensions are straightforward to add:
- Resend confirmation. After the invoice is sent, call the Resend API via an HTTP Request node to send yourself a plain-text summary — client name, service booked, and invoice amount — so you know the billing completed.
- Event-based pricing. Use a Switch node to map Cal.com event type slugs to different Stripe Price IDs, making each service type bill at its configured rate.
- Invoice audit log. Append each successful run to a Supabase table with the
bookingUid, Stripe invoice ID, amount, and creation timestamp. This gives you a billing history outside Stripe's dashboard. - Failed-payment follow-up. Add a second n8n workflow triggered by Stripe's
invoice.payment_failedwebhook to send an automatic follow-up to clients whose card declined.
The Broader Picture
This workflow is a reusable pattern for any booking-to-billing integration. The same structure works if you replace Cal.com with Calendly, or add a payment-required step before the booking is confirmed. The core idea — webhook event in, structured invoice out — is one of the most reliable automation patterns in n8n because both ends speak clean JSON with no polling required.
If you need pre-built billing and booking templates to start from, the library at n8nresources.dev/templates includes workflows you can import and adapt. A subscription gives you access to the full collection.
Enjoyed this article?
Share it with others who might find it useful