# 05 · Flows

Each flow lists its steps, the screens involved and the API calls behind them, followed by a Mermaid diagram. The prototype should be able to click through every one of these from start to finish using `data.js`.

---

## 1. Missed call → voicemail → call back

1. A customer calls the business number. Nobody answers.
2. The server texts the caller back automatically. A `Call` row with `kind: missed_auto_texted` is created, and a `system` message "Missed call from …" appears in the conversation.
3. The caller leaves a voicemail. The call becomes `kind: voicemail`, and the conversation (`channel: voicemail`) gets `meta.voicemail { duration_seconds, audio_url }`. The transcript arrives a little later.
4. **Push alert:** "Voicemail from Greg Hollis (0:38)". Tapping it opens the voicemail thread.
5. The owner sees the voicemail card. They can play it (only if `audio_url` exists) and read the transcript (or "No transcript. Press play to listen.").
6. Remy's suggestion card offers a reply ("…I can come tomorrow 8–10 AM. Okay?"), and Remy may have pencilled in a slot (booking card, pending).
7. The owner taps **Call back**. `POST /calls` rings the owner's phone first, and the banner says "Your phone is about to ring. Answer it and we'll connect Greg."
8. If the call can't be placed: "The call could not be placed" → **Dial from this phone** (with a warning that Greg will see the owner's own number) / **Not now**.
9. After the call: the owner taps **Confirm & text Greg** on the booking card. `POST /appointments/{id}/status` → confirmed, with the success haptic and a toast.

```mermaid
flowchart TD
  A[Customer calls] --> B{Answered?}
  B -- yes --> Z[Call kind: answered]
  B -- no --> C[Server auto-texts caller<br/>Call kind: missed_auto_texted]
  C --> D{Leaves voicemail?}
  D -- no --> E[Push: Missed call from Dana<br/>Thread shows missed-call card]
  D -- yes --> F[Call kind: voicemail<br/>meta.voicemail set]
  F --> G[Push: Voicemail from Greg 0:38]
  G --> H[Voicemail thread opens on card]
  H --> I{audio_url?}
  I -- yes --> J[Play + transcript]
  I -- no --> K[No play button<br/>transcript only]
  J --> L[Call back]
  K --> L
  E --> L
  L --> M[POST /calls rings owner first]
  M -- ok --> N[Banner: your phone is about to ring]
  M -- fails --> O[Dial from this phone? warning]
  N --> P[Confirm pencilled-in job<br/>POST /appointments/id/status confirmed]
```

## 2. New website lead → Remy replies → owner books

1. A visitor chats on the website. Remy answers, asks the qualifying questions and takes a name and number. A `Lead` is created (`source: ai_chat`, `channel: web`, `conversation_id: web:<session>`).
2. Remy offers a free slot. The visitor accepts, and Remy creates an appointment (`confirmed`) or pencils one in (`pending`).
3. **Push:** "New lead: Raj Okafor · EV charger survey".
4. The owner opens Today. The hero shows the new lead. Tap → lead detail (Remy's summary, Worth/When).
5. If it was pencilled in, it is a ghost slot on Jobs › Calendar. **Accept & text Raj** → confirmed.
6. If not booked, **Book a job**: the date, then FREE TIMES (`GET /appointments/available-slots`), then **Book** (`POST /appointments`).
7. To keep talking by text: in the thread, **Continue by text** (`POST /conversations/migrate-channel`). If the server answers 409 for consent, the consent sheet appears; only `no_consent` offers the attestation.

```mermaid
sequenceDiagram
  participant V as Website visitor
  participant R as Remy (server)
  participant O as Owner app
  V->>R: "Do you install EV chargers?"
  R->>V: Qualifying questions, offers Thu 2:00 PM
  V->>R: Accepts, gives number
  R->>R: Create Lead (ai_chat, web) + Appointment
  R-->>O: Push "New lead: Raj Okafor"
  O->>O: Today hero → Lead detail
  alt Remy pencilled it in (pending)
    O->>R: POST /appointments/{id}/status confirmed
  else Not booked
    O->>R: GET /appointments/available-slots
    O->>R: POST /appointments
  end
  R->>V: Confirmation text
  opt Continue by text
    O->>R: POST /conversations/migrate-channel
    R-->>O: 409 consent? → Consent sheet → POST /leads/{id}/consent
  end
```

## 3. Remy suggestion → approve or edit

1. A customer message arrives. Depending on the business's mode:
   - **Off:** nothing.
   - **Suggest a reply** (default): a suggestion card appears under the last message.
   - **Answer the first few:** auto-send for the first 4 replies, then suggest.
   - **Send automatically:** auto-send every time.
2. **Send:** the reply goes out (`POST /leads/{id}/reply`). The bubble appears "Sending…" and then "Delivered", and the owner gets the success haptic. Because a human replied, Remy now holds off this conversation (owner takeover). The toast says so.
3. **Edit:** the text moves into the composer, the card goes away, and the owner changes the text and sends.
4. **Dismiss:** the card goes away. Nothing is sent.
5. **Auto-send:** "Remy is sending in 5…" with **Stop**, over a 5-second bar. If Stop is tapped, the text moves into the composer and auto-send stops for this thread. Otherwise it sends.
6. A suggestion never shows when the conversation is opted out, held by a teammate, on a closed WhatsApp window or on web chat.

```mermaid
stateDiagram-v2
  [*] --> Inbound: customer message
  Inbound --> Nothing: mode off / opted out / held / window closed
  Inbound --> Card: mode suggest
  Inbound --> Countdown: mode auto (or first 4 replies)
  Card --> Sent: Send
  Card --> Composer: Edit
  Card --> [*]: Dismiss
  Countdown --> Sent: 5 s elapse
  Countdown --> Composer: Stop
  Composer --> Sent: owner sends
  Sent --> RemyHolds: server sets owner takeover
  RemyHolds --> [*]
```

## 4. Pause or resume Remy in a thread

1. The owner opens a thread → "…" → **Pause Remy here** (`POST …/pause`, 1 hour by default). The "Remy is paused here" banner appears with **Resume**. Inbox rows show a "Remy paused" chip.
2. Replying yourself has the same effect, for 30 days (a takeover). The banner says "You're answering this one."
3. **Resume** (`POST …/unpause`, idempotent) → the banner goes away, with the toast "Remy is answering again".
4. A teammate holding the conversation (`takeover_by_name`) shows "Jess is replying" in place of the composer. **Take over** (`POST …/takeover`) moves the lock to you, and Remy stays paused.
5. The same action is on the Inbox row's long-press menu.

```mermaid
stateDiagram-v2
  [*] --> RemyActive
  RemyActive --> Paused1h: Pause Remy here (POST pause)
  RemyActive --> Takeover30d: Owner replies (auto takeover)
  Paused1h --> RemyActive: Resume (POST unpause) or 1 h passes
  Takeover30d --> RemyActive: Resume (POST unpause)
  RemyActive --> HeldByTeammate: teammate takes over
  HeldByTeammate --> Takeover30d: Take over (POST takeover)
  HeldByTeammate --> RemyActive: lock goes stale after 15 min of no activity
```

## 5. New owner signup → number → texting approval waiting (proposed)

1. The owner gets an invite link (or signs up on the website). They create their login.
2. **Your business:** name, trade, area, hours.
3. **Your number:** pick a new local number, or keep their own and forward missed calls (with carrier instructions).
4. **Texting approval:** the carrier registration is submitted. **Waiting state:** "Texting is being approved… usually 3–7 days. Calls, voicemail and web chat work now." Remy can answer calls and web chat in the meantime; SMS replies are disabled with that reason.
5. **Your assistant:** greeting, urgent words, missed-call text.
6. **Alerts** primer → **Done:** "Remy is answering." + Make a test call.
7. When approval lands: a push "Texting is on. Remy can now text your customers back." If changes are needed: a push plus the step re-opens with what to fix.

```mermaid
flowchart LR
  S[Invite / sign up] --> B[Your business]
  B --> N[Your number]
  N --> T[Texting approval submitted]
  T --> W{{Waiting 3–7 days<br/>calls + web chat live<br/>SMS disabled with reason}}
  W -- approved --> P[Push: Texting is on]
  W -- needs changes --> F[Fix details] --> T
  N --> A[Your assistant] --> AL[Alerts primer] --> D[Done: Remy is answering]
```

## 6. Session expired

1. Any request answers 401. The app tries `POST /auth/refresh` once.
2. If the refresh succeeds, it retries silently.
3. If the refresh fails, the app signs out **once**. Drafts, tokens and the on-device cache are wiped. Sign-in shows "You were signed out. Please sign in again." with the business code prefilled. Warning haptic.
4. After signing in again, the app returns to Today (it does not try to restore a half-written draft).

```mermaid
flowchart TD
  R[Any API call] --> U{401?}
  U -- no --> OK[Continue]
  U -- yes --> RF[POST /auth/refresh]
  RF -- ok --> RT[Retry the call] --> OK
  RF -- fails --> SO[Sign out once<br/>wipe drafts, tokens, cache]
  SO --> SI[Sign in: You were signed out. Please sign in again.]
  SI --> T[Today]
```

## 7. Push tap → deep link

Push alerts carry one link: the website inbox URL `/client/{c}/admin/inbox/{sms|whatsapp|web}/{phone or session}`. The app turns it into a conversation key: `sms:<last 10 digits>`, `whatsapp:<last 10>`, `web:<session>`. Call and voicemail alerts link to `/sms/`, so the app falls back to `voicemail:<last 10>`. The `call:{phone}` tag makes a missed-call alert *replace* the incoming-call alert.

1. Tap the alert → switch to the **Inbox** tab, clear its stack, and push the matching thread.
2. No match → refresh the inbox once and try again.
3. Still no match → stay on the Inbox list (no error).
4. Signed out → sign in first, then follow the link.
5. A banner arriving while the app is open shows as an in-app glass banner. Tapping it follows the same path.

| Alert | Example text | Opens |
|---|---|---|
| New message | "Dana Whitfield: It tripped again just now…" | `#/thread/sms:5550184471` |
| New lead | "New lead: Raj Okafor · EV charger survey" | `#/thread/web:w_8f2a91c4` |
| Missed call | "Missed call from (555) 012-3370. Remy texted them back." | `#/thread/sms:…` or the voicemail thread |
| Voicemail | "Voicemail from Greg Hollis (0:38)" | `#/voicemail/voicemail:5550142290` |

```mermaid
flowchart TD
  P[Push tapped] --> L{Signed in?}
  L -- no --> S[Sign in] --> K
  L -- yes --> K[Parse url → conversation key]
  K --> I[Switch to Inbox, clear stack]
  I --> M{Key in inbox?}
  M -- yes --> T[Push thread]
  M -- no --> R[Refresh inbox once] --> M2{Found now?}
  M2 -- yes --> T
  M2 -- no --> L2[Stay on Inbox list]
```
