# Feature: Messaging

## Purpose

Team-scoped in-app conversations (staff ↔ staff, staff ↔ portal customers) plus queued **service** SMS via ClickSend with configurable templates. Chat messages do not send SMS.

## Boundaries

- **Owns:** `conversations`, `conversation_participants`, `messages`, `sms_templates`, `sms_dispatches`; Messaging Actions, Policies, queued jobs, Inertia pages under `Pages/Messaging/`
- **Does not own:** CRM phones (reads `customers.phone` via soft DB), Jobs appointment lifecycle (soft-consumed by Jobs via `AppointmentSmsHooks`)
- **Depends on (platform only):** User / Team / auth / Queue; ClickSend credentials in `config/services.php` (`CLICKSEND_USERNAME`, `CLICKSEND_API_KEY`, optional `CLICKSEND_FROM`)
- **Depends on (other domains):** soft customer phone/name
- **Consumed by (soft):** Jobs hooks after create/update/cancel appointment when Messaging is registered
- **Platform UI:** Messages nav in `AppLayout.vue`; SMS templates linked from Company Settings

## Models

| Model | Table | Notes |
|-------|-------|-------|
| `Conversation` | `conversations` | `team_id`, `created_by_user_id`, optional `subject` |
| `ConversationParticipant` | `conversation_participants` | `user_id`, `last_read_at` |
| `Message` | `messages` | `sender_user_id`, `body` |
| `SmsTemplate` | `sms_templates` | `trigger_key`, `body`, `is_enabled`, optional `reminder_hours` |
| `SmsDispatch` | `sms_dispatches` | audit: `queued` / `sent` / `failed` / `skipped` |

Migrations: `database/migrations/2026_08_12_160000_create_messaging_tables_and_sms_settings.php`; `database/migrations/2026_08_29_140000_drop_sms_gateway_email_from_company_settings.php` (removed the old per-team Email-to-SMS gateway column).

### Permissions

- Owners / Jetstream `admin`: view all team conversations; can reply (auto-joined on send)
- Others: only conversations they participate in
- Portal `customer`: reply only (cannot start); staff/admin/editor/owner start threads
- SMS templates: same restriction as company settings (not staff/customer)

### SMS triggers

| Key | When |
|-----|------|
| `appointment.scheduled` | Appointment created as scheduled |
| `appointment.en_route` | Status → `en_route` |
| `appointment.completed` | Status → `completed` |
| `appointment.cancelled` | Cancelled |
| `appointment.reminder` | Delayed queue job at `starts_at - reminder_hours` (default 24) |

ClickSend: queued `SendServiceSms` POSTs `https://rest.clicksend.com/v3/sms/send` with HTTP Basic (username + API key). Body is the rendered template; `to` is the customer mobile normalised to E.164 (UK `07…` / `7…` → `+44…`). Optional `CLICKSEND_FROM` sets the sender ID. Dispatch is skipped when credentials are missing, the template is disabled, or there is no recipient phone.

Placeholders: `{{customer_name}}`, `{{job_title}}`, `{{appointment_starts_at}}`, `{{team_name}}`, `{{staff_name}}`.

## Routes

### Web

| Method | URI | Name |
|--------|-----|------|
| GET | `/messaging` | `messaging.index` |
| POST | `/messaging` | `messaging.store` |
| GET | `/messaging/{conversation}` | `messaging.show` |
| POST | `/messaging/{conversation}/messages` | `messaging.messages.store` |
| GET | `/messaging/sms-templates` | `messaging.sms-templates.index` |
| PUT | `/messaging/sms-templates/{smsTemplate}` | `messaging.sms-templates.update` |

### API

| Method | URI | Name |
|--------|-----|------|
| GET | `/api/messaging` | `api.messaging.index` |
| POST | `/api/messaging` | `api.messaging.store` |
| GET | `/api/messaging/{conversation}` | `api.messaging.show` |
| POST | `/api/messaging/{conversation}/messages` | `api.messaging.messages.store` |

## Tests

- Path: `laravel/tests/Feature/Domains/Messaging/`
- Cover: ACL, send message, SMS queue when ClickSend+phone+enabled, skip when disabled/unconfigured, reminder scheduled, ClickSend HTTP success/failure
- Tests leave `CLICKSEND_*` empty so appointment flows skip SMS (same as the old missing gateway). Messaging tests that expect a send set dummy credentials and `Http::fake` the REST API.

## Add / remove checklist

### Add

- [x] Domain + provider registered
- [x] Wiki linked from README
- [x] Feature tests

### Remove

- [ ] Unregister provider
- [ ] Delete domain, wiki, tests, nav link, Jobs soft hooks `use`/`class_exists` blocks
- [ ] Drop messaging tables (and the historic `sms_gateway_email` drop migration)
- [ ] Unset `CLICKSEND_*` from `.env.example` / `config/services.php` / `phpunit.xml`
- [ ] Suite still green
