VTAP REST API
Tap your phone, take your itinerary with you
PythonFlaskpySerialNFC hardware
- Built
- September – October 2025
- Status
- In use on kiosk hardware
- Stack
- Python, Flask, pySerial, python-dotenv
- Code
- Private — happy to walk through it
Problem
A visitor stands at a kiosk and builds an itinerary — the places they want to see, in the order they want to see them. Then they need to take it with them, because an itinerary stuck on a screen in a lobby is no use to anyone.
The standard answer is a QR code, and the standard answer barely works. Scanning one means getting your phone out, opening the right app, framing the code, and deciding to trust the link — four chances to give up. Adoption was low enough that people were walking away from the kiosk without the thing they had just spent five minutes making.
A VTAP reader removes all four steps: tap the phone, the link opens. But the reader is configured over a serial cable with ASCII commands, which is fine if you set one URL and walk away. Here every visitor needs a different one, changing as they build their plan — and the kiosk application has no business opening a COM port to make that happen.
Solution
A small Flask service that owns the serial port and exposes it as something any application can call. The kiosk builds an itinerary URL for this visitor, posts it, and the next tap hands over exactly that.
POST /with a JSON body containing aurlchanges the link the reader hands out; the kiosk makes an HTTP request and forgets the hardware exists- Wrong method or a missing field returns a real 405 or 400 rather than accepting it and quietly doing nothing — on a kiosk, silent failure is the bug you find three weeks later
- The serial helpers are also a standalone CLI: enumerate ports, open a session, send raw commands. Hardware can be brought up and tested on a bench before any application exists to drive it
- The COM port comes from the environment, so the same code runs on the bench machine and the deployed one without edits
Worth noting
Splitting it into a library, a CLI, and a service was the decision that
paid off. The same change_url() is what the CLI calls and
what the endpoint calls, so testing the hardware and testing the
integration are never testing two different code paths.
It also means diagnosing a problem on site starts with one command against the reader, instead of trying to work out whether the kiosk app or the hardware is the one lying to you.