Small business
From Business Card to Booked Call
Most of our first conversations with new clients start somewhere other than a website. Someone meets us at a networking event or a coffee shop, takes a business card, and means to get in touch later. That’s where a lot of good leads get lost. So the back of our card has a QR code, and scanning it lets that person book a consultation on the spot, while they still have the card in their hand.
This post walks through how that works. We built it for our own business with Google Workspace, which many small businesses already use, plus a small piece of code running on Cloudflare. The same setup works for any business that books appointments, from a salon to a law office.
The QR code opens the consultation form on our site with the consultation option already selected. The form asks for a name, an email address and, optionally, a phone number, followed by a few short questions about the person’s current website, their goals and what’s getting in their way. Those answers mean the first call starts with context instead of twenty minutes of background. The last step is picking a time. The form shows open hour-long slots from our real calendar, and the person chooses whether to meet over Google Meet, by phone or in person.
The open times come straight from the calendar. Our working hours are written down once in a short, readable list, and the booking script checks that list against what’s already on the calendar before offering anything:
weekly: {
1: [['09:00', '17:00']], // Monday
2: [['09:00', '17:00'], ['18:00', '20:00']], // Tuesday + evening
// ...the rest of the week trimmed for space
6: [['10:00', '13:00']], // Saturday morning
},
The earliest slot it offers is two days out, which leaves time to read the answers before the call, and it looks about three weeks ahead. Changing our hours means editing that list, and anything already on the calendar, like a client meeting or a dentist appointment, is never offered as open.
When the person presses submit, the form goes to a Cloudflare Worker, a small program that runs on Cloudflare’s network. It checks that the request came from a person rather than a bot, using Cloudflare’s Turnstile check. Then it saves the submission to its own database before doing anything else. That way, if Google is slow to answer for a moment, the lead is still safe, and the Worker records whether the handoff to Google went through. The Worker passes the details to a Google Apps Script with a shared secret, so the script ignores anything that didn’t come from our own Worker.
Google Apps Script is the part of Google Workspace that lets code work directly with your Sheets, Calendar and Gmail, and it does the rest of the job. First it adds a row to our leads spreadsheet, which serves as a simple CRM, with the person’s answers and the time they picked. Then it creates the calendar event and invites the person, so Google sends them its usual calendar invitation. If they chose Google Meet, the script asks Calendar to attach a Meet link to the event:
if (b.format === 'meet') {
resource.conferenceData = {
createRequest: {
requestId: Utilities.getUuid(),
conferenceSolutionKey: { type: 'hangoutsMeet' },
},
};
opts.conferenceDataVersion = 1;
}
A phone call gets the person’s number as the event location, and an in-person meeting gets a note that we’ll pick a spot together. The script also sends the person a confirmation email with the time, the format, the Meet link if there is one, and a link to pay the $25 booking fee, which goes toward the first hour. A copy of the booking lands in our own inbox, filed under a label so it’s easy to find.
Until the $25 booking fee is paid, the event title starts with “[UNPAID]”, so we can tell at a glance which times are confirmed. When the payment comes through, the row gets marked Paid in the spreadsheet, and that one edit sends the person a receipt and removes the marker from the calendar. You can mark payments yourself, which is how we do it because our bank’s checkout page doesn’t send payment notifications to other software. If your payment provider offers a payment webhook, the Worker can check the provider’s signature and mark the row Paid on its own. If the fee hasn’t arrived within 48 hours, a check that runs every hour frees the slot, marks the row Expired and sends the person a friendly note saying the time was released and they’re welcome to pick a new one.
There are a few small things we’d suggest to anyone building something similar. Print the QR code large enough to scan from arm’s length, and test it with a couple of different phones before ordering a box of cards. Point it at a page on your own site rather than a third-party scheduling link, so you can change what happens after the scan without reprinting anything. Keep the form short enough to finish standing up. And send every lead somewhere you’ll see it, because a lead that sits unread for a week has often moved on.
This is everything a single scan does. Someone takes our card at a coffee shop, scans it before they’ve left the table, answers a few questions and picks a Tuesday afternoon. Within moments there’s a row in our CRM with everything they told us, a calendar invite on both calendars with a Meet link attached, a confirmation in their inbox and an alert in ours. Nobody had to type anything into a spreadsheet or copy details from one app to another. It runs on the Google Workspace account we already had and a small Cloudflare Worker, with no separate scheduling subscription.
If you hand out business cards, flyers or yard signs and want more of them to turn into booked appointments, this is the kind of system we build at Kirby Web Works. We’re glad to look at how your customers book with you now and set up something that fits.