Workshop · Systems of my own

Mail scanners click the confirmation link first

A booking page of my own next to my site: a visitor picks half an hour, confirms it from their inbox, and we both get the Meet link and a calendar entry. Most of the decisions came from the mail.

Written by
Kristóf Karner — independent developer, Budapest
Updated
6 November 2026
“a fraudster should in no way be able to book more than one slot; let’s filter it so that only real people, and only once, can book” Kristóf Karner, 31 October 2026 — translated from Hungarian

The booking page runs on a subdomain of this site, since 29 October 2026. It is built in PHP and SQLite (a database in a single file), with no framework and no outside packages. One small script makes the picking fluid, and it is also what tells a person from a machine: browsing works without it, booking does not.

Before it, a free booking tool sat on my site. In the summer of 2025 a later client of mine booked a call through it, without writing first. What I took from that is that some people would rather talk than write, and I don't want to lose them. So my own booking page is about one thing: that no booking gets lost quietly.

What the mail decided

Confirming is a button, not a link. Many mail systems' security scanners open every link before the person does, to see where it leads. If the confirmation link confirmed by itself, the scanner would approve the booking. So the link in the mail only opens a page with a single button, and only pressing it (a form submission) changes anything. The login link of the admin panel works the same way.

After sending, the page moves on. Safari on a phone reloads the page when the visitor comes back from the mail app. If the booking page stayed in front of it, the reload would send the form a second time. So after sending, another page comes (by a redirect), and it also says the mail may be in the spam folder.

What doesn't come back, I hear about. The chosen slot is held for the visitor for two hours. If they don't confirm by then, the booking doesn't disappear without a word: I get its details. The confirmation may have landed in spam, or the address may have a typo. If I don't write to them, a client is lost either way.

Both sides know the same. After confirming, the visitor gets the Meet link, a cancel link and a calendar file (.ics), on an iPhone offering the phone's own calendar first. I get every detail, and if I reply, the mail goes straight to them. Two hours before the call a reminder goes out, unless they booked only a few hours before.

Cancelling and moving work from the mail, within limits: twice at most, only to a later time, and not within the notice before the call. Thirty days after the call, every trace of the booking is deleted.

A calendar that doesn't need filling in

In the first version every day had to be filled in on its own. I asked that “I should only have to edit the days that need to be arranged differently”. Since then a standing weekly rule holds, one that doesn't run out, and only a day that differs goes in, as an exception. A Hungarian public holiday can't be booked: the booking page works the list out itself from section 102 of the Hungarian Labour Code, Easter and Pentecost included, so there is nothing to do with it from year to year.

There is no daily cap either. Instead I asked that after a conversation ends, there be no other one for at least an hour. The rule counts both ways: an hour is needed before a call too, or an earlier booking would run into it. A weekday can be booked a day ahead, a weekend two days ahead, so that I know in time. If too many people turn up at once, a switch closes booking: the page then says that no time can be booked, and that email is welcome. Calls already booked stay.

A picker that doesn't move

On a phone, the day and the time are picked on one screen, without a reload. The times stand in fixed cells: changing the day only changes which ones are free, so the page doesn't jump. A single frame travels to the next day or time, and if I tap again on the way, it carries on from where it is. It works without a script too, only then picking is a page change.

Logging in without a password

The admin panel has no password that could be spied on or guessed. Logging in is a one-time link that goes only to my address, and even there it sits behind a button. On my own device I can stay logged in: the session lives in a cookie that scripts can't read and that never goes into the address bar, so not into the browser history and not onto a screenshot. One button logs me out of every device. Visitors get no cookie at all.

At the top of the panel there is a calendar: four weeks, day by day, in words. A day opens in place, its half hours can be ticked one by one, and saving happens without a reload.

Against abuse

The booking page is a public form that sends mail. A calendar filled with junk, or a stranger's inbox flooded, would take my domain's reputation with it. So one person can book one call, and only a real person. The booking page doesn't even tell a stranger whether an address already has a call: the reason for a refusal only goes by mail. I don't write the exact rules down, because the way around them could be read from them too. Every new feature starts with the question of how it could be misused.

What it measures

An anonymous event log counts what happens: where the visitor comes from, how far they get on the page, how many bookings are confirmed, expire, are cancelled or moved, and which call nobody came to. It holds no IP address, email address or name, so it can stay without a time limit. The booking links on my site carry their source, so I can see which page the calls come from. A summary arrives by mail every month.

A part of the site

The booking page runs on a separate subdomain because my site is static: its server refuses all PHP, and if the booking page failed, it shouldn't reach the site's files. The header, the footer, the styles and the fonts are copied over from the site by a script, so the booking page is part of the site, not a look-alike.

The booking page is in Hungarian only, because the calls are in Hungarian too. If someone opens an English page from Hungary, a dismissable Hungarian sentence offers them the Hungarian site and the booking. For that, the booking page answers one question: is the visitor's address registered in Hungary. It works the answer out from public data of the RIPE NCC (the registry of European IP addresses), stores nothing, and asks no third party.

368 automated checks run through the whole flow, with a hand-moved clock: from booking, through cancelling and the reminder, to deletion.

Where I decided

I built it with AI. I decided what it should do, and how I check it. The booking page is Hungarian only; there is no booking on the English pages. After the first version of the picker I asked for it to be more fluid, and for us to rethink the whole thing. In one version the page slid to the form by itself after a time was picked; that caused confusion, so it went, and the visitor scrolls on their own. The first call is free, and the filtering shouldn't scare off a real client: if a rule stands in a person's way, email is always open.

My other systems, and the connections between them, are on the Systems of my own map. What a business gets from this is in the piece written for clients.

Questions on the topic

Why not a ready-made booking tool?

A ready-made tool is a good choice if what it does is enough. I needed it on my own domain, with my site's look, without cookies or someone else's brand, and so that I hear about unconfirmed bookings too.

Why is it in Hungarian only?

Because the calls are in Hungarian. English-speaking visitors write an email: the language switch on the booking page leads to the English contact page of my site.

Does it need a cookie banner?

Visitors get no cookie, so the booking page doesn't need one. The privacy notice still has to say what it stores, what for, and for how long.

← Back to Systems of my own