Buchungsseiten (CalendarPage)
Zweck
CalendarPage ist die öffentliche Terminbuchungs-Seite pro Mitarbeiter — Calendly-ähnlich, aber direkt aus SpeamCore heraus mit Bezug zu Arbeitszeit-Modellen und Abwesenheiten. Gäste rufen die Seite ohne SpeamCore-Login auf, wählen einen Meeting-Type (z. B. „15 Minuten Vorgespräch"), sehen freie Slots und buchen mit Namen + E-Mail.
Pro Mitarbeiter eine Buchungsseite (1:1).
Voraussetzungen
Berechtigungen (CASL)
| Action | Subject | Wirkung |
|---|---|---|
view | CalendarPage | Admin-Liste + Detail aufrufbar |
update | CalendarPage | Einstellungen ändern (Begrüßungstext, Buffer, Slot-Fenster) |
view / update / delete | CalendarMeetingType | Meeting-Types verwalten |
view / update | Employee | Page-Gate + Verantwortung für eigene CalendarPage |
Die Public-Endpoints (/ca/:slug/*) brauchen keine Authentifizierung.
Datenmodell
CalendarPage
| Feld | Pflicht | Typ | Wirkung |
|---|---|---|---|
employeeId | ja | UUID → Employee | Inhaber der Seite |
slug | auto bei Create | String (URL-safe, random 8–10 Zeichen) | Public-URL /ca/{slug}. Unveränderbar nach Anlage. |
active | ja | Boolean, default true | false → /ca/:slug liefert 404 |
welcomeText | nein | TEXT | Begrüßungs-Text auf der Public-Seite |
bufferMinutes | nein | Integer, default 0 | Pause zwischen aufeinanderfolgenden Slots |
minNoticeMinutes | nein | Integer, default 60 | Mindestvorlauf — Slots „in der nächsten Stunde" verschwinden |
maxLeadDays | nein | Integer, default 60 | Wie weit voraus überhaupt gebucht werden kann |
dayStartMinutes | nein | Integer, default 540 (= 09:00) | Frühester Slot-Beginn (Ortszeit) |
dayEndMinutes | nein | Integer, default 1020 (= 17:00) | Spätestes Slot-Ende (Ortszeit) |
CalendarMeetingType
| Feld | Pflicht | Typ | Wirkung |
|---|---|---|---|
name | ja | String 255 | Anzeigename („30 Minuten Termin") |
description | nein | TEXT | Erklärtext im Picker |
durationMinutes | ja | Integer, default 30 | Slot-Länge |
location | nein | String 512 | Adresse, Telefonnummer oder Video-Link (z. B. Zoom) |
color | nein | Hex-String | Akzent-Farbe im Picker |
active | ja | Boolean, default true | false → blendet den Typ im Picker aus |
position | nein | Integer | Sortier-Reihenfolge |
Default-Seed beim Anlegen einer neuen Page: 15 Min (grün), 30 Min (blau), 60 Min (violett).
CalendarBooking
| Feld | Wirkung |
|---|---|
startAt, endAt | Zeitraum in UTC |
attendeeFirstName, -LastName, -Email | Daten des Buchenden |
attendeePhone, subject | optional |
status | confirmed (Default) | cancelled |
accessKey | Token, mit dem der Gast seine Buchung später wieder aufruft (via E-Mail-Link) |
cancellationReason, cancelledAt | Audit bei Storno |
Verfügbarkeits-Engine
Der Public-Endpoint GET /api/ca/:slug/availability berechnet pro Zeitraum (max. maxLeadDays voraus) alle freien Slots:
- Zeit-Range validieren —
minNoticeMinutesschiebt den Start nach vorn,maxLeadDaysdeckelt das Ende. - Arbeitszeit-Modell laden — pro Wochentag
WorkTimeModelDay.coreTimeStartMinutes/-Endaus dem aktiven Vertrag. - Abwesenheiten mit
status = approvedsperren ganze Tage (siehe Abwesenheiten). - Slot-Fenster pro Tag =
max(dayStartMinutes, coreTimeStart) … min(dayEndMinutes, coreTimeEnd). - Slots ausspielen mit Schrittweite =
durationMinutes; nach jedem Slot zusätzlichbufferMinutesPause. - Existierende Buchungen mit
status = confirmedblockieren überlappende Slots (inkl. Buffer-Zone). - Ergebnis: Liste
[ { startAt, endAt }, … ]in UTC.
Zeitzonen-Hinweis: Die Minuten-Werte in WorkTimeModelDay und CalendarPage sind in Ortszeit des Servers, die Buchungs-Zeitstempel werden in UTC ausgeliefert. Der Public-Page-Picker konvertiert pro Browser-Locale.
Schritt-für-Schritt-Anleitung
Buchungsseite aktivieren
/calendar-pagesöffnen → Mitarbeiter ohne Page erscheinen mit Button „+ Seite anlegen".- SpeamCore generiert automatisch:
- einen Slug (z. B.
m6r3aef9) - die drei Standard-Meeting-Types (15/30/60 Minuten)
- Slot-Fenster
dayStartMinutes/-Endaus dem Arbeitszeit-Modell des Mitarbeiters
- einen Slug (z. B.
- Optional
welcomeText,bufferMinutes,minNoticeMinutespflegen. - Active = true setzen, sobald die Seite live gehen soll.
Meeting-Types anpassen
In der Detail-Seite unter Meeting-Types:
- Neuen Typ hinzufügen (z. B. „60 Min Strategie-Call" mit eigener Farbe).
- Bestehende Typen umbenennen, deaktivieren, Reihenfolge ändern (
position). locationkann Adresse, Telefonnummer oder Zoom/Teams-Link sein — wird in der Bestätigungsmail an den Gast verschickt.
Slug + Public-URL
Der Slug ist nach Anlage unveränderbar (analog zu QR-Links und Visitenkarten). Die Public-URL lautet:
https://app.speamcore.com/ca/{slug}
Verknüpfung mit anderen Modulen:
- Visitenkarte des Mitarbeiters → zeigt automatisch einen „Termin buchen"-Button mit Verweis auf die CalendarPage, wenn beide aktiv sind.
- QR-Link → kann mit
targetType = "url"und der CalendarPage-URL alstargetUrldirekt zur Buchung führen.
Public-Workflow für Gäste
- Gast öffnet
/ca/{slug}— sieht Foto, Name, Begrüßungstext, Meeting-Type-Auswahl. - Wählt einen Meeting-Type → Slot-Picker zeigt Monatsnavigation und freie Zeiten.
- Wählt einen Slot → Buchungsformular: Vor-/Nachname, E-Mail, Telefon (optional), Notiz.
- Klick „Termin buchen" →
POST /api/ca/:slug/book. - Server erstellt
CalendarBookingmitaccessKey, schickt Bestätigungs-Mail. - Gast landet auf
/ca/bookings/:accessKey— Bestätigungsseite mit Termin-Details und ICS-Download (.ics-Datei direkt importierbar in Outlook/Google/Apple).
Storno / Reschedule durch Gast
Der Gast erreicht über den accessKey-Link (in der Mail) jederzeit seine Buchung und kann sie:
- Stornieren —
DELETE /api/ca/bookings/:accessKey(optional mitcancellationReason). - Verschieben —
POST /api/ca/bookings/:accessKey/reschedule(Slot-Picker erneut, Buchung wird umgesetzt).
Verknüpfungen zu anderen Modulen
- Visitenkarten — verlinken zur CalendarPage des Mitarbeiters über „Termin buchen"-Button.
- QR-Links — können zur CalendarPage zeigen (
targetType = "url"mit der/ca/:slug-URL). - Arbeitszeit-Modelle — Quelle für
coreTimeStartMinutes/-Endzur Slot-Berechnung. - Abwesenheiten — sperren ganze Tage in der Slot-Engine.
- Kalender — interne Termin-Sicht; CalendarPage-Buchungen erscheinen dort als reguläre Termine.
- Mitarbeiter — Inhaber der Buchungsseite, 1:1.
API/Schnittstellen
Admin (auth)
| Methode | Endpoint | Zweck | CASL |
|---|---|---|---|
GET | /api/calendar-pages | Liste aller Buchungsseiten | view Employee |
GET | /api/calendar-pages/:id | Detail | view Employee |
PATCH | /api/calendar-pages/:id | Settings ändern | update Employee |
GET | /api/calendar-pages/:id/meeting-types | Meeting-Types des Mitarbeiters | view Employee |
POST | /api/calendar-pages/:id/meeting-types | Meeting-Type anlegen | update Employee |
PATCH | /api/calendar-meeting-types/:id | Meeting-Type ändern | update Employee |
DELETE | /api/calendar-meeting-types/:id | Meeting-Type entfernen | update Employee |
Public (keine Auth)
| Methode | Endpoint | Zweck |
|---|---|---|
GET | /api/ca/:slug | Resolved Page + Meeting-Types + Employee-Daten |
GET | /api/ca/:slug/photo | Profilbild-Stream |
GET | /api/ca/:slug/availability?meetingTypeId=&from=&to= | Verfügbare Slots |
POST | /api/ca/:slug/book | Neue Buchung anlegen + Mail |
GET | /api/ca/bookings/:accessKey | Detail für den Gast |
DELETE | /api/ca/bookings/:accessKey | Stornieren |
GET | /api/ca/bookings/:accessKey/ics | ICS-Download |
POST | /api/ca/bookings/:accessKey/reschedule | Termin verschieben |
Häufige Fehler und Lösungen
| Fehler | Lösung |
|---|---|
/ca/:slug zeigt 404 | active = false oder Page gelöscht. Im Editor reaktivieren oder neu anlegen. |
| Slot-Picker zeigt kaum freie Termine | dayStartMinutes/dayEndMinutes zu eng gesetzt, oder das Arbeitszeit-Modell des Mitarbeiters hat enge coreTime-Fenster. Beides prüfen. |
| Gast bekommt keine Bestätigungs-Mail | Mail-System des Mandanten prüfen (siehe Mail-Konten). attendeeEmail ggf. typo. |
| Abwesenheits-Tag erscheint trotzdem buchbar | Absence.status muss approved sein — requested blockiert nicht. |
| Doppel-Buchung im gleichen Slot | Sollte durch Buffer-/Overlap-Check unmöglich sein. Wenn doch: in /calendar-Modul beide Termine prüfen, manuell einen stornieren. |
| Slug-Eingabe disabled | Korrekt — nach Anlage unveränderbar, damit gedruckte Links stabil bleiben. |
Versionshinweise
- 2026-05-21 (Welle 138): Initiale Veröffentlichung. Quelle: BE
80fb7ca0(3 ModelsCalendarPage/-MeetingType/-Booking, 8 Admin- + 8 Public-Endpoints), FE1c4bb924(Admin-Editor + Public-Page mit Slot-Picker, Bestätigungs-Seite mit ICS-Download / Reschedule / Cancel). Slot-Berechnung gegenWorkTimeModelDay+Absence-Sperren,bufferMinutes/minNoticeMinutes/maxLeadDaysals Steuerungs-Felder. Public-Page nutzt das in Welle 137 eingeführtePublicPageShell. Default-Seed: 3 Standard-Meeting-Types beim Anlegen einer neuen Page.