Venevio.
Reservierungssystem für Gastronomiebetriebe, betrieben als mandantenfähiges SaaS: Ein Betrieb bucht ein Abo und bekommt eine eigene Buchungsseite — unter eigener Domain, als Subdomain, als Pfad oder eingebettet in die bestehende Website. Kern des Produkts ist kein Kalender, sondern der Umgang mit dem teuersten Problem der Branche: Gäste, die nicht erscheinen.
und Betrieb — allein
62 SQL-Migrationen
Supabase, Stripe
venevio.de
Ausgangslage
Das eigentliche ProblemEin reserviertes Tischpaar, das nicht erscheint, kostet doppelt: Der Tisch bleibt leer, und die Gäste, die man dafür abgewiesen hat, sind auch weg. Am Freitagabend um 20 Uhr trifft das härter als am Dienstagmittag — und bei acht Personen härter als bei zweien.
Die naheliegende Antwort wäre, für jede Reservierung eine Kaution zu verlangen. Das funktioniert in der Praxis nicht: Wer für zwei Personen am Dienstag zwanzig Euro hinterlegen soll, bucht woanders. Die Lösung muss also unterscheiden können — nach Gästezahl, Wochentag, Uhrzeit und danach, wie sich dieser Gast in der Vergangenheit verhalten hat.
Genau das war die Produktentscheidung: nicht das hundertste Reservierungsformular, sondern ein Regelwerk, das der Betrieb selbst einstellt und das im Normalfall unsichtbar bleibt.
No-Show-Schutz
KernstückJeder Betrieb hinterlegt eine Regel: ab wie vielen Gästen, an welchen Wochentagen, in welchem Zeitfenster, wie viel pro Person — und einen Deckel nach oben. Eine Buchung wird gegen diese Regel geprüft, bevor überhaupt nach einer Zahlung gefragt wird. Fällt sie durch eines der Kriterien, ist keine Kaution fällig und der Gast merkt vom ganzen System nichts.
-- eine Regel pro Betrieb, vom Betreiber im Dashboard gepflegt min_guest_count INTEGER DEFAULT 6 -- ab 6 Personen amount_per_person INTEGER DEFAULT 1000 -- 10,00 € in Cent max_deposit_amount INTEGER DEFAULT 2000 -- Deckel: 20,00 € days_of_week INTEGER[] DEFAULT '{5,6}' -- nur Fr + Sa time_range_start TIME DEFAULT '18:00' time_range_end TIME DEFAULT '22:00' high_risk_override BOOLEAN DEFAULT true -- Risiko sticht Regel -- Betrag, gedeckelt: amount = min(gäste × amount_per_person, max_deposit_amount)
Der Deckel ist wichtiger, als er aussieht. Ohne ihn zahlt eine Zwölfergruppe 120 Euro Kaution und bucht nicht — obwohl gerade große Gruppen das eigentliche Risiko sind. Mit Deckel bleibt die Kaution ein Signal, keine Hürde.
Nach dem Besuch läuft die Rückerstattung von selbst: Ein täglicher Cronjob sucht bezahlte Kautionen zu vergangenen Terminen, erstattet sie über Stripe zurück und schickt eine Bestätigung. Bleibt die Reservierung als No-Show markiert, verfällt der Betrag — auch darüber wird der Gast informiert. Der Betreiber muss in beiden Fällen nichts tun.
Gästereputation
Ohne KlarnamenDamit „hat schon dreimal nicht abgesagt" überhaupt bewertbar ist, führt das System eine Historie pro Gast — betriebsübergreifend. Das ist datenschutzrechtlich der heikelste Teil des Produkts, also liegt in dieser Tabelle keine Telefonnummer und keine E-Mail-Adresse im Klartext:
hashed_phone VARCHAR(64) -- Hash, kein Klartext hashed_email VARCHAR(64) -- Hash, kein Klartext total_bookings INTEGER no_shows INTEGER cancellations INTEGER
Nachgeschlagen wird über den Hash der eingegebenen Kontaktdaten. Wer die Datenbank liest, sieht Zahlen und Hashes — aber keine Liste von Personen. Daraus entsteht beim Buchen eine Einstufung in low, medium oder high, die auf der Reservierung gespeichert wird. Steht die Regel auf high_risk_override, kann diese Einstufung eine Kaution auslösen, die sonst nicht fällig wäre.
Wer wiederholt nicht erscheint, landet auf einer Sperrliste. Das ist die Sorte Funktion, bei der die Umsetzung mehr über Verantwortung entscheidet als über Technik — deshalb Hashes statt Klartext und eine Einstufung, die begründbar bleibt, statt einer undurchsichtigen Punktzahl.
Mandantenfähigkeit
Ein System, viele BetriebeVier Wege zur richtigen Buchungsseite
Das System erkennt den Betrieb an der Subdomain (mario.venevio.de), an einer eigenen Domain des Kunden (pizzamario.de), am Pfad (/r/pizza-mario) — oder liefert die Seite als eingebettetes Widget in eine bestehende Website. Vier Einstiege, eine Codebasis.
Trennung in der Datenbank, nicht im Code
Jede relevante Tabelle trägt eine restaurant_id, und Row Level Security in Postgres setzt die Grenze durch. Eine vergessene Abfragebedingung im Anwendungscode kann damit keine fremden Reservierungen ausliefern — die Datenbank gibt sie schlicht nicht heraus.
Vier Tarife, ein Regelwerk
Basic, Premium, Professional und Ultra unterscheiden sich in freigeschalteten Funktionen. Welcher Tarif was kann, steht in einer Tabelle statt in Bedingungen quer durch den Code; die Oberfläche fragt an einer Stelle nach und blendet entsprechend frei.
Geld fließt am Betreiber vorbei
Abogebühren laufen über Stripe an Venevio. Kautionen dagegen gehören dem Restaurant — sie laufen über Stripe Connect auf dessen eigenes Konto. Der Betrieb verbindet sein Konto im Dashboard selbst; die Plattform hält fremdes Geld nie.
Ein Cronjob für alles Wiederkehrende
Statt fünf Zeitplänen läuft ein täglicher Lauf, der alles Fällige abarbeitet: abgelaufene, nie bestätigte Reservierungen löschen, Erinnerungen per E-Mail und WhatsApp verschicken, Kautionen erstatten oder verfallen lassen, Bewertungsanfragen versenden. Ein Einstiegspunkt, ein Log, ein Ort für Fehler.
Empfehlungen mit echter Abrechnung
Wer einen Betrieb wirbt, bekommt 25 % Provision auf jede monatliche Zahlung. Ausgelöst wird das nicht beim Vertragsabschluss, sondern bei jedem erfolgreichen Zahlungseingang über den Stripe-Webhook — Provision entsteht also nur, wenn auch wirklich Geld geflossen ist.
Funktionsumfang
Was der Betrieb bekommtBuchungsseite
Eigene Seite mit Logo und Farben des Betriebs, wahlweise eingebettet in die bestehende Website.
Tische & Zeitblöcke
Tischverwaltung mit Konfliktprüfung, dazu Öffnungszeiten und Sperrzeiten für Feiertage oder private Feiern.
Manuelle Freigabe
Optional landet jede Anfrage zuerst beim Betrieb: E-Mail mit allen Angaben, Freigabe oder Ablehnung per Klick.
Erinnerungen
Automatische Erinnerung vor dem Termin per E-Mail und WhatsApp — das wirksamste Mittel gegen vergessene Reservierungen.
Gutscheine
Verkauf über Stripe, Zustellung als PDF, Einlösung im Haus über einen QR-Scanner im Dashboard.
Auswertungen
Auslastung, Stornoquote und No-Show-Quote als Diagramme, dazu Gästelisten mit Filtern und Sortierung.
Aktionscodes
Rabattcodes mit Gültigkeitsprüfung und Einlöseprotokoll, getrennt von den Gutscheinen.
Abo-Verwaltung
Tarifwechsel mit Vorschau auf die anteilige Verrechnung, Kündigung und Reaktivierung — alles im Dashboard.
Eigene Domain
Kunden verbinden ihre Domain selbst; die Zuordnung und Prüfung läuft automatisch über die Domain-Schnittstelle der Hosting-Plattform.
Technik & Entscheidungen
Und was sie gekostet habenDer Stack: Next.js 16 mit React 19 auf Vercel, Oberfläche mit Tailwind und Radix-Bausteinen, Daten und Authentifizierung über Supabase (Postgres mit Row Level Security) plus NextAuth, Zahlungen über Stripe inklusive Connect und Webhooks, E-Mails über Resend, Fehlerüberwachung mit Sentry. Rund 257 JavaScript-Dateien, 70 API-Routen, 62 SQL-Skripte.
Regeln in Daten statt in Code. Kautionsbedingungen, Tariffunktionen und Öffnungszeiten liegen als Zeilen in der Datenbank, nicht als Bedingungen im Anwendungscode. Ein Betrieb, der freitags ab vier Personen kassieren will, braucht damit keine Anpassung im Code — nur einen anderen Datensatz. Der Preis: mehr Prüflogik zur Laufzeit und Migrationen, die man sauber schreiben muss.
Ein Cronjob statt vieler. Fünf separate Aufgaben wären einzeln einfacher zu verstehen. Ein gemeinsamer Lauf ist dafür an einer Stelle nachvollziehbar, wenn nachts etwas schiefgeht — bei einem Projekt, das eine Person betreibt, zählt das mehr als saubere Trennung.
Kautionen über Stripe Connect statt über das eigene Konto. Der bequemere Weg wäre gewesen, das Geld selbst einzuziehen und weiterzuleiten. Das hätte aus dem Projekt ein Treuhandgeschäft mit allem gemacht, was daran hängt. Über Connect gehört das Geld dem Betrieb — die zusätzliche Einrichtung pro Kunde war es wert.