Client work
Tatycan
A pet-grooming salon in Galapagar, Madrid. We built both halves of its software: the public site where customers book, and the phone-first CRM where the day is run — the agenda, every pet’s history and photos, the money, and Spain’s digital invoicing rules, all in the groomer’s pocket.
- 2 independent systems — a public site and a CRM
- 67 API endpoints, every input validated
- 5 min background cadence — reminders that never double-send
The ask
A real business with real customers, and software that finally fits how the work is done.
Tatycan grooms dogs and cats in Galapagar, on the edge of Madrid. The salon wanted what off-the-shelf tools wouldn’t give it: booking that understands breeds and sizes, records organized around pets rather than invoices, reminders that reach customers where they actually read them, and invoicing that satisfies Spain’s new digital-invoicing rules. So they asked for software of their own.
We built it as two small systems instead of one big one — and that separation is most of the story.
What we built
A public half that stores nothing, and a private half that runs the whole salon.
tatycan.com is the public half: fast server-rendered pages in Spanish — services, prices, gallery — and a booking form that validates the breed, size, and service combination and computes the price on the server, so the quote a customer sees is the salon’s real price list, not a guess. A finished booking doesn’t write to a database; it sends the salon a Telegram message. For a one-person salon a booking is the start of a conversation, not a transaction — so the public site holds no accounts and no customer data, and has nothing to breach.
crm.tatycan.com is the private half: the salon’s operating system, built for a phone held in one hand between dogs. Pets are the primary record — owners hang off pets, not the other way around. The agenda blocks double-bookings, every visit carries photos and notes, the finance view knows the day’s takings against categorized expenses, and a completed visit can become a fiscal record the Spanish tax agency can verify. Only names and phone numbers are stored: nothing more is needed, so nothing more is kept.
How it works
Two systems, deliberately separate — they meet in the groomer’s pocket.
Keeping the halves apart is the design, not an accident. The public site cannot lose customer data because it never holds any; the CRM never has to trust input from the open internet. Behind the CRM, one background engine wakes every five minutes during salon hours and does three jobs: WhatsApp appointment reminders, booking confirmations, and the nightly tax paperwork. Each job claims its work atomically — the database itself guarantees a reminder can only be claimed once — so an overlapping or retried run can never message a customer twice.
Inside the CRM
Real product, replaced data: these screens run against a seeded demo dataset — never real customers.
The hard part: invoices a tax agency can verify
Spain’s VeriFactu rules turn invoicing software into a cryptography problem. We shipped it for a one-person salon.
Under VeriFactu (Royal Decree 1007/2023), invoicing software must produce tamper-evident records: every fiscal record carries a SHA-256 fingerprint of the record before it, forming a chain that cannot be edited without breaking. The CRM issues the day’s simplified invoices nightly, renders each record to the agency’s XML format, and submits it over a mutual-TLS connection. Once a visit is invoiced, its billing fields lock.
Two details we would defend anywhere: the chain re-checks its head inside the database write itself, so two invoices issued at the same moment cannot fork the chain; and the hash implementation is pinned to the tax agency’s published test vectors in the test suite, so conformance is a passing test rather than an opinion.
Decisions worth noting
- A booking is a message, not a system. The public site notifies the salon over Telegram instead of writing to a database — no accounts to manage, no data to breach, right-sized for a one-person salon.
- Dates that cannot lie. Instants are stored as UTC; the Madrid calendar date is computed once, in shared code, and stored beside them — so the agenda and the finance view group by plain dates and stay correct from any device in any timezone. Money is integer cents everywhere.
- The URL is the state. The active tab, the selected day, the list filters all live in the address bar — every screen survives browser-back and can be shared as a link, and nothing snaps back unexpectedly.
- Inert until configured. WhatsApp messaging and VeriFactu both ship feature-flagged: empty configuration means off, so no test or preview environment can ever message a real customer or emit a real fiscal record.
- Photos handled like they matter. Compressed in the browser before upload, location metadata stripped, and served only through the authenticated API — the photo storage is never public.
Built with
- Cloudflare Workers
- Hono
- React
- D1
- R2