End-to-End Dokumentation: Friseur 1

Company: Friseur 1 (ID: 1) | Branches: 1 | Services: 3 | Staff: 5

Diese Dokumentation beschreibt den vollständigen E2E-Prozess von Inbound-Calls über Retell.ai, Middleware, Cal.com bis zur Buchungsbestätigung. → Vollständige Spezifikation | 📊 Audit-Report | ⚠️ Gaps

📱 AskPro AI - Produkt-Übersicht

Voice AI Platform für automatische Terminbuchungen
Multi-Tenant SaaS-Lösung mit KI-gestütztem Telefon-Agent für Friseursalons, Arztpraxen, und Service-Unternehmen.

0a. Anbieter-Perspektive AskPro AI

Was bietet AskPro AI als Plattform-Anbieter: Multi-Tenant SaaS, Voice AI Integration, Kalender-Synchronisation, Automatisches Billing.

graph TB subgraph "🏢 AskPro AI Platform" PLATFORM["⚙️ Laravel Middleware
(Multi-Tenant Core)"] ADMIN["🖥️ Filament Admin
(Company Management)"] BILLING["💳 Stripe Billing
(Usage-Based)"] DB[("🗄️ PostgreSQL
(Shared Database)")] end subgraph "🔌 Externe Integrationen" RETELL["🤖 Retell.ai
(Voice AI Platform)"] CALCOM["📅 Cal.com
(Booking System)"] end subgraph "👥 Endkunden (Mandanten)" FRISEUR["💈 Friseur 1
(2 Filialen, 5 Mitarbeiter)"] ARZT["🏥 Arztpraxis
(Terminbuchung)"] SERVICE["🔧 Service-Betrieb
(Auftragsannahme)"] end PLATFORM --> DB ADMIN --> PLATFORM PLATFORM --> BILLING PLATFORM <--> RETELL PLATFORM <--> CALCOM FRISEUR --> RETELL ARZT --> RETELL SERVICE --> RETELL PLATFORM -.->|"Mandant 1"| FRISEUR PLATFORM -.->|"Mandant 2"| ARZT PLATFORM -.->|"Mandant 3"| SERVICE style PLATFORM fill:#E8F5E9 style ADMIN fill:#E0F2F1 style BILLING fill:#FCE4EC style DB fill:#F1F8E9 style RETELL fill:#FFF3E0 style CALCOM fill:#F3E5F5 style FRISEUR fill:#E3F2FD style ARZT fill:#E3F2FD style SERVICE fill:#E3F2FD

Kernfunktionen: Multi-Tenancy (Company Scoping), Voice AI Integration (Retell.ai), Kalender-Sync (Cal.com), Usage-Based Billing (Stripe), Admin-Portal (Filament).

0b. Endkunden-Perspektive Value Proposition

Was erhält ein Endkunde wie "Friseur 1": 24/7 Telefon-Agent, Automatische Buchungen, Keine manuelle Verwaltung, Integration mit bestehendem Kalender.

graph LR subgraph "😊 Kunde (Anrufer)" CALL["📞 Anruf bei
+493033081738"] end subgraph "🤖 AskPro AI Service" AGENT["🎙️ KI-Agent
spricht Deutsch
versteht Intent"] BOOK["📅 Automatische
Terminbuchung
24/7 verfügbar"] end subgraph "💈 Friseur 1 (Ihr Unternehmen)" CAL["📆 Ihr Kalender
(Cal.com)
bleibt immer aktuell"] TEAM["👥 Ihr Team
keine manuelle
Eingabe nötig"] CONFIRM["✅ Bestätigung
per E-Mail/SMS
an Kunden"] end CALL --> AGENT AGENT -->|"Termin gefunden"| BOOK BOOK --> CAL CAL --> TEAM BOOK --> CONFIRM style CALL fill:#E3F2FD style AGENT fill:#FFF3E0 style BOOK fill:#C8E6C9 style CAL fill:#F3E5F5 style TEAM fill:#E0F2F1 style CONFIRM fill:#C8E6C9

Vorteile für Friseur 1:
✅ Keine verpassten Anrufe (24/7 Erreichbarkeit)
✅ Automatische Buchung ohne Personal (Kosteneinsparung)
✅ Integration mit bestehendem Kalender (keine Doppelbuchungen)
✅ Natürliche Sprachverarbeitung (Kunden merken kaum einen Unterschied)
✅ Transparente Abrechnung (nutzungsbasiert, keine Fixkosten)

A. C4-Kontextdiagramm System Overview

Zeigt alle beteiligten Systeme und deren Kommunikation: Customer, Retell.ai, Middleware, Cal.com, Frontend, Billing, Database.

graph LR CUS[👤 Customer] RAI["🤖 Retell.ai
Agent: ...b6d56ab07b"] MW["⚙️ Middleware
Laravel API"] CAL["📅 Cal.com
Team: 34209"] FE["🖥️ Frontend
Filament"] BILL["💳 Billing
Stripe"] DB[("🗄️ Database
PostgreSQL")] CUS -->|"Inbound Call
+493033081738"| RAI RAI -->|Webhook| MW MW -->|"Booking API"| CAL MW -->|Read/Write| DB MW -->|UI| FE MW -->|Charges| BILL CAL -->|Webhook| MW style CUS fill:#E3F2FD style RAI fill:#FFF3E0 style MW fill:#E8F5E9 style CAL fill:#F3E5F5 style FE fill:#E0F2F1 style BILL fill:#FCE4EC style DB fill:#F1F8E9

📖 Details in e2e.md

B. E2E Happy Path Main Flow

Erfolgreicher Buchungspfad: Anruf → Intent-Erkennung → Policy-Check → Verfügbarkeitsabfrage → Buchung → Bestätigung → Logging.

flowchart TB subgraph CUS["👤 Customer"] A["Anruf: +493033081738"] end subgraph RAI["🤖 Retell.ai"] B["Greet: 'Guten Tag bei Friseur 1'"] C{"Known
Customer?"} D[collect_appointment_info] E[check_availability] end subgraph MW["⚙️ Middleware"] F["PolicyEngine:
canBook?"] G["CalcomService:
getSlots"] H{"Slot
found?"} I[Create Appointment] J[Log Call + Costs] end subgraph CAL["📅 Cal.com"] K["Query Availability
Team 34209"] L["POST /bookings"] M[("Booking DB")] end subgraph FE["🖥️ Frontend"] N["Display in
Call Log"] O["Show Transcript"] end A --> B --> C C -->|"Phone Lookup"| F C -->|Yes| D --> E F -->|"Policy OK"| G G --> K K -->|"Slots Available"| H H -->|Yes| I I --> L --> M M -->|"Webhook Confirm"| I I --> J J --> N --> O H -->|No| D F -->|"Policy Block"| J style A fill:#E3F2FD style B fill:#FFF3E0 style C fill:#FFE0B2 style F fill:#C8E6C9 style H fill:#C8E6C9 style I fill:#C8E6C9 style K fill:#E1BEE7 style M fill:#F1F8E9

📖 Details in e2e.md

C. Alternativpfade Error Handling

Umgang mit Sonderfällen: Umbuchung, Stornierung, Policy-Blockierung, No-Show, Nummernunterdrückung, Eskalation.

flowchart TB START["Call Eingang"] subgraph "Erfolgreiche Pfade" BOOK["✅ Neu-Buchung"] RESC["✅ Umbuchung"] end subgraph "Fehler-Pfade" CANCEL["Stornierung"] POLICY["❌ Policy-Block"] NOSHOW["No-Show"] CLIR["🔒 Nummer unterdrückt"] HUMAN["👤 Eskalation Mensch"] end START --> BOOK START --> RESC START --> CANCEL START --> CLIR CANCEL -->|"nach Cutoff"| POLICY RESC -->|"3x überschritten"| POLICY POLICY -->|"Log + Info"| END["Call Ende"] CLIR -->|Fallback| HUMAN BOOK -->|"15min später"| NOSHOW NOSHOW -->|"Auto-Mark"| LOG["Log No-Show"] style BOOK fill:#C8E6C9 style RESC fill:#C8E6C9 style POLICY fill:#FFCDD2 style NOSHOW fill:#FFECB3 style CLIR fill:#E1BEE7

📖 Details in e2e.md

D. Conversational Decision Tree Intent Recognition

Intent-Erkennung, Required Slots, Disambiguation, NLU-Fallback-Strategien.

flowchart TB START["Greet Customer"] INTENT{"Intent
erkannt?"} BOOK["Intent: book_appointment"] CANCEL["Intent: cancel_appointment"] RESC["Intent: reschedule_appointment"] INFO["Intent: get_info"] UNK["Intent: unknown"] START --> INTENT INTENT -->|book| BOOK INTENT -->|cancel| CANCEL INTENT -->|reschedule| RESC INTENT -->|info| INFO INTENT -->|unclear| UNK BOOK --> SLOTS["Required Slots:
service, date, time"] SLOTS --> DISAMB{"All
filled?"} DISAMB -->|No| ASK["Ask missing info"] ASK --> SLOTS DISAMB -->|Yes| CONFIRM["Confirm & Book"] UNK --> FALLBACK["NLU Fallback:
Rephrase question"] FALLBACK --> INTENT style START fill:#E3F2FD style INTENT fill:#FFF3E0 style BOOK fill:#C8E6C9 style CONFIRM fill:#C8E6C9 style UNK fill:#FFCDD2 style FALLBACK fill:#FFE0B2

📖 Details in e2e.md

E. Middleware-Orchestrierung Integration Layer

Webhook-Verarbeitung, Idempotenz, Retry-Logic, Mapping (TeamID↔Filiale, EventID↔Service), Timezone-Handling.

flowchart TB subgraph "Webhook Empfang" W1["Retell Webhook
/api/webhooks/retell"] W2["Cal.com Webhook
/api/calcom/webhook"] end subgraph "Idempotenz" ID1["Check:
retell_call_id exists?"] ID2["Check:
calcom_booking_uid exists?"] end subgraph "Verarbeitung" MAP["Mapping:
TeamID↔Branch
EventID↔Service
StaffID↔User"] POL["Policy Check
canBook/canCancel"] RETRY["Retry Logic
Circuit Breaker"] end subgraph "Persistence" DB[("Database
PostgreSQL")] CACHE[("Redis Cache
5min TTL")] end W1 --> ID1 W2 --> ID2 ID1 -->|New| MAP ID2 -->|New| MAP ID1 -->|Duplicate| SKIP["Return cached"] ID2 -->|Duplicate| SKIP MAP --> POL POL --> RETRY RETRY --> DB DB --> CACHE style ID1 fill:#FFF3E0 style ID2 fill:#FFF3E0 style MAP fill:#E1BEE7 style POL fill:#FFE0B2 style SKIP fill:#C8E6C9

📖 Details in e2e.md

F. Cal.com Sequence Booking API

Sequenzdiagramm der Cal.com API-Integration: Availability → Slot Selection → Booking Creation → Webhook Callback.

sequenceDiagram participant MW as Middleware participant CAL as Cal.com API
Team 34209 Note over MW,CAL: Availability Check MW->>CAL: GET /slots/available
eventTypeId=???
startTime=2025-11-04 CAL-->>MW: {slots: [...]} Note over MW,CAL: Booking Creation MW->>CAL: POST /bookings
{eventTypeId, start, attendee} CAL-->>MW: {uid, id, status: "accepted"} MW->>MW: Store calcom_booking_id Note over MW,CAL: Webhook Callback CAL->>MW: POST /api/calcom/webhook
{triggerEvent: "BOOKING_CREATED"} MW-->>CAL: 200 OK Note over MW,CAL: Rescheduling MW->>CAL: PATCH /bookings/{uid}
{start: new_time} CAL-->>MW: {uid, status: "rescheduled"} CAL->>MW: Webhook: BOOKING_RESCHEDULED Note over MW,CAL: Cancellation MW->>CAL: DELETE /bookings/{uid} CAL-->>MW: 204 No Content CAL->>MW: Webhook: BOOKING_CANCELLED

📖 Details in e2e.md

G. Billing Flow Cost Calculation

Sekundengenaue Kostenberechnung: Retell.ai Cost → Platform Markup → Currency Conversion → Rounding → Balance/Invoice.

flowchart TB START["Call ends"] DUR["Duration:
per_second"] COST["Retell Cost:
seconds × $0.020/60"] MARKUP["Markup:
× 30%"] EXCH["Convert:
USD → EUR"] ROUND["Round:
ceil(cents)"] BAL{"Prepaid
Balance?"} DEDUCT["Deduct from Balance"] TOPUP["Stripe Top-Up
required"] INV["Add to Invoice"] START --> DUR --> COST COST --> MARKUP --> EXCH --> ROUND ROUND --> BAL BAL -->|Sufficient| DEDUCT BAL -->|Low| TOPUP DEDUCT --> INV TOPUP --> INV style START fill:#E3F2FD style COST fill:#FFE0B2 style BAL fill:#FFF3E0 style DEDUCT fill:#C8E6C9 style TOPUP fill:#FFCDD2

📖 Details in e2e.md

H. Telemetrie & Logging Observability

Call-ID-Korrelation, Metriken (Duration, Cost, Sentiment), Transcript-Speicherung, Privacy (PII Redaction).

flowchart LR subgraph "Correlation" CALL["Call ID
retell_call_id"] BOOK["Booking ID
calcom_booking_id"] CUS["Customer ID"] end subgraph "Metrics" DUR["Duration
(seconds)"] COST["Cost
(cents)"] SENT["Sentiment
(-1 to 1)"] INTENT["Intent
(book/cancel/...)"] end subgraph "Logs" TRANS["Transcript
(TEXT)"] AUDIO["Audio URL
(30 days)"] META["Metadata JSON"] end subgraph "Privacy" ANON["PII Redaction
after 365d"] DEL["Auto-Delete
after 90d"] end CALL --> BOOK CALL --> CUS CALL --> DUR CALL --> COST CALL --> SENT CALL --> INTENT CALL --> TRANS CALL --> AUDIO CALL --> META TRANS --> ANON --> DEL style CALL fill:#E3F2FD style BOOK fill:#E1BEE7 style TRANS fill:#FFF3E0 style ANON fill:#FFCDD2

📖 Details in e2e.md

I. Zustandsautomat State Machine

Lifecycle eines Leads/Kunden: Unknown → Known → In Gespräch → Slot gesucht → Gebucht → Verschoben/Storniert/Completed/NoShow.

stateDiagram-v2 [*] --> Unknown: First Contact Unknown --> Known: Phone Lookup Success Unknown --> Lead: New Customer Created Known --> InGespraech: Call Connected Lead --> InGespraech: Call Connected InGespraech --> SlotGesucht: Intent Book InGespraech --> Abgebrochen: Hang Up SlotGesucht --> Gebucht: Slot Found and Confirmed SlotGesucht --> KeineSlots: No Availability KeineSlots --> SlotGesucht: Try Different Time KeineSlots --> Abgebrochen: Give Up Gebucht --> Bestaetigt: Confirmation Sent Bestaetigt --> Verschoben: Reschedule Request Bestaetigt --> Storniert: Cancel Request Bestaetigt --> Completed: Appointment Occurred Bestaetigt --> NoShow: Customer Absent 15min Verschoben --> Bestaetigt: New Time Confirmed Completed --> [*] Storniert --> [*] NoShow --> [*] Abgebrochen --> [*]

📖 Details in e2e.md

J. ER-Diagramm Data Model

Datenmodell: Company → Branch → Service (+ Components) → Staff → Customer → Appointment → Call → Transcript → Charge.

erDiagram COMPANY ||--o{ BRANCH : "has" COMPANY ||--o{ POLICY_CONFIGURATION : "defines" COMPANY ||--o{ SERVICE : "offers" BRANCH ||--o{ PHONE_NUMBER : "has" BRANCH ||--o{ STAFF : "employs" BRANCH ||--o{ APPOINTMENT : "hosts" SERVICE ||--o{ SERVICE_COMPONENT : "contains (TODO)" SERVICE }o--o{ STAFF : "via service_staff" STAFF }o--|| BRANCH : "works_in" STAFF ||--o{ APPOINTMENT : "performs" CUSTOMER ||--o{ APPOINTMENT : "books" CUSTOMER ||--o{ CALL : "makes" APPOINTMENT }o--|| SERVICE : "for" APPOINTMENT }o--|| STAFF : "with" APPOINTMENT }o--|| BRANCH : "at" CALL ||--o| CUSTOMER : "made_by" CALL ||--o| APPOINTMENT : "creates" CALL ||--o{ TRANSCRIPT : "has" CALL ||--|| CHARGE : "incurs" COMPANY ||--o{ PREPAID_BALANCE : "maintains (TODO)" COMPANY { uuid id PK string name "Friseur 1" json settings "business_type: hair_salon" } BRANCH { uuid id PK uuid company_id FK string name "Zentrale/Zweigstelle" json settings "calcom_team_id: 34209" } SERVICE { uuid id PK string name "Service name" int duration_minutes "30-180" json settings "calcom_event_type_id: ???" } SERVICE_COMPONENT { uuid id PK "NOT IMPLEMENTED" uuid service_id FK string name int duration_minutes bool requires_staff bool staff_reuse_allowed } STAFF { uuid id PK string name string email int calcom_user_id "1001-1005" } PHONE_NUMBER { string phone_number "+493033081738" string retell_agent_id "agent_b36ecd..." uuid branch_id FK }

📖 Details in e2e.md

K. Terminarten Simple vs. Composite

Simple Appointments: Einfache Termine mit einem durchgehenden Zeitraum (z.B. Beratungsgespräch 60min).
Composite Appointments: Mehrteilige Termine mit Arbeitsphasen und optionalen Pausen (z.B. Färben mit Einwirkzeit).

K.1 Taxonomie & Datenmodell

classDiagram direction TB class Appointment { +uuid id +string title +datetime starts_at +datetime ends_at +boolean is_composite +json segments +string composite_group_uid +isComposite() bool +getSegments() array } class SimpleAppointment { +is_composite = false +segments = null } class CompositeAppointment { +is_composite = true +segments = [...Segment] } class Segment { +datetime start +datetime end +string label +string type "work|break" +int order +int duration_minutes } Appointment <|-- SimpleAppointment Appointment <|-- CompositeAppointment CompositeAppointment "1" *-- "1..n" Segment : contains in JSON note for CompositeAppointment "Segmente als JSON-Array gespeichert:\n[\n {start, end, label, type, order},\n ...\n]"

Implementierung: Composite Appointments speichern ihre Segmente als JSON-Array im segments-Feld. Keine separate Tabelle nötig. starts_at/ends_at umfassen die gesamte Zeitspanne (min/max aller Segmente).

K.2 Zeitachse mit Pausen

gantt title Terminarten im Vergleich (Beispiel 03.11.2025) dateFormat HH:mm axisFormat %H:%M section Simple Appointment Beratungsgespräch (60min) :done, s1, 09:00, 60m section Composite Appointment Färben Phase 1 (45min) :active, c1, 10:30, 45m Einwirkzeit PAUSE (30min) :crit, c2, after c1, 30m Auswaschen Phase 2 (20min) :active, c3, after c2, 20m Föhnen Phase 3 (25min) :active, c4, after c3, 25m

Beispiel: Ein Färbe-Termin besteht aus 4 Segmenten (3× WORK, 1× BREAK). Gesamtdauer: 120min. Pausen sind explizit als eigene Segmente modelliert.

K.3 Validierungs-Flow

flowchart TB START([Termin erstellen]) START --> TYPE{Terminart?} TYPE -->|Simple| SIMPLE[is_composite = false
segments = null] SIMPLE --> VALIDATE_SIMPLE[Start/End validieren] VALIDATE_SIMPLE --> SAVE TYPE -->|Composite| COMPOSITE[is_composite = true] COMPOSITE --> SEGMENTS[Segmente definieren] SEGMENTS --> PAUSE{Pausen benötigt?} PAUSE -->|Ja| ADD_BREAKS[BREAK-Segmente einfügen
type: 'break'] PAUSE -->|Nein| ORDER ADD_BREAKS --> ORDER ORDER[Segmente sortieren
order: 1, 2, 3...] ORDER --> VALIDATE_SEG[Validierung] VALIDATE_SEG --> CHECK1{Überlappungen?} CHECK1 -->|Ja| ERROR1[❌ Fehler] CHECK1 -->|Nein| CHECK2{Lücken?} CHECK2 -->|Ja| WARN[⚠️ Warnung] CHECK2 -->|Nein| CALC WARN --> CALC CALC[starts_at = min Segment start
ends_at = max Segment end] CALC --> SAVE SAVE[(Appointment speichern)] ERROR1 --> END([Ende]) SAVE --> END style SIMPLE fill:#C8E6C9 style COMPOSITE fill:#E1BEE7 style ADD_BREAKS fill:#FFE0B2 style ERROR1 fill:#FFCDD2 style WARN fill:#FFF9C4 style SAVE fill:#C8E6C9

Validierungsregeln:

K.4 State Machine (Laufzeit)

stateDiagram-v2 [*] --> Scheduled: Termin erstellt Scheduled --> InProgress: start() Scheduled --> Cancelled: cancel() InProgress --> Paused: pause()
(bei BREAK-Segment) InProgress --> Completed: finish() InProgress --> Cancelled: cancel() Paused --> InProgress: resume()
(nächstes WORK-Segment) Paused --> Cancelled: cancel() Completed --> [*] Cancelled --> [*] note right of Paused Bei Composite Appointments: Pause = BREAK-Segment aktiv end note note right of InProgress Aktuelles Segment wird verarbeitet (WORK) end note

Hinweis: Bei Composite Appointments steuern pause() und resume() den Übergang zwischen WORK- und BREAK-Segmenten.

K.5 Datenbank-Schema

erDiagram APPOINTMENT { uuid id PK uuid company_id FK uuid branch_id FK uuid service_id FK uuid customer_id FK uuid staff_id FK string title datetime starts_at datetime ends_at boolean is_composite json segments string composite_group_uid string status datetime created_at datetime updated_at datetime deleted_at } SERVICE ||--o{ APPOINTMENT : fuer CUSTOMER ||--o{ APPOINTMENT : bucht STAFF ||--o{ APPOINTMENT : fuehrt_durch BRANCH ||--o{ APPOINTMENT : in COMPANY ||--o{ APPOINTMENT : gehoert_zu

Persistenz: Alle Appointment-Daten in einer Tabelle. Segmente als JSON im segments-Feld. composite_group_uid erlaubt Gruppierung mehrerer Termine (z.B. für komplexe Behandlungen).

segments JSON-Struktur (Beispiel):

[
  {
    "start": "2025-11-03 10:30:00",
    "end": "2025-11-03 11:15:00",
    "label": "Färben Phase 1",
    "type": "work",
    "order": 1,
    "duration_minutes": 45
  },
  {
    "start": "2025-11-03 11:15:00",
    "end": "2025-11-03 11:45:00",
    "label": "Einwirkzeit",
    "type": "break",
    "order": 2,
    "duration_minutes": 30
  }
]