# Email and Notification System — Full Audit

**Date:** 2025-02-25  
**Scope:** nomade-back (Laravel)  
**Type:** Analysis only — no code changes.

---

## 1. List of All Email Send Points

### 1.1 Delivery mechanism

| Mechanism | File | Behavior |
|-----------|------|----------|
| **SendEmailJob::dispatch()** | `app/Jobs/SendEmailJob.php` | Job is **queued** (implements `ShouldQueue`). When the job runs, it calls `Mail::to($to)->cc($cc)->send($mailable)` — i.e. **sync send at job execution time**. |
| **MailingService::sendEmailAt()** | `app/Services/MailingService.php` | Schedules `SendEmailJob` with a delay; email is sent when the delayed job runs (queued, then sync send). |
| **Mail::to()->send()** (direct) | `app/Jobs/SendCollabReminder.php` | Job is queued; inside the job, email is sent synchronously. |
| **VerifyEmail** | `app/Notifications/VerifyEmailNotification.php` | Uses Laravel’s `toMail()`; sent via `$user->notify(new VerifyEmailNotification)` (see Notifications). |

All emails sent through **MailingService** go through **SendEmailJob** (either immediate dispatch or delayed via `sendEmailAt()`). There is no direct `Mail::to()->send()` in controllers; controllers call **MailingService** methods.

### 1.2 Email send points (by caller)

| # | File | Method | Mailable / Email | Queued? | Notes |
|---|------|--------|------------------|---------|--------|
| 1 | MailingService | sendEmail | GenericEmail | Yes (SendEmailJob) | Generic subject/view/data |
| 2 | MailingService | sendLeadRegistrationLinkEmail | LeadRegistrationLinkEmail | Yes | |
| 3 | MailingService | sendRegistrationRequestCompany | RegistrationRequestCompanyEmail | Yes | |
| 4 | MailingService | sendRegistrationRequestNomade | RegistrationRequestNomadeEmail | Yes | |
| 5 | MailingService | sendCanceledColabInfluencerEmail | CanceledColabEmail | Yes | |
| 6 | MailingService | sendInfluencerCanceledPendingCompanyEmail | InfluencerCanceledPendingCompanyEmail | Yes | |
| 7 | MailingService | sendDateChangedColabInfluencerEmail | DateChangedColabEmail | Yes | |
| 8 | MailingService | sendDataChangedColabInfluencerEmail | DataChangedColabEmail | Yes | |
| 9 | MailingService | sendDateChangedColabCompanyEmail | DateChangedColabEmail | Yes | |
| 10 | MailingService | sendDataChangedColabCompanyEmail | DataChangedColabEmail | Yes | |
| 11 | MailingService | sendRegistrationInfluencerEmail | RegistrationInfluencerEmail | Yes | |
| 12 | MailingService | sendRegistrationInfluencerAdminEmail | RegistrationInfluencerAdminEmail | Yes | |
| 13 | MailingService | sendRegistrationCompanyAdminEmail | RegistrationCompanyAdminEmail | Yes | |
| 14 | MailingService | sendCompanyRegistrationReceivedEmail | CompanyRegistrationReceivedEmail | Yes | |
| 15 | MailingService | sendCompanyRegistrationOnboardingEmail | CompanyRegistrationOnboardingEmail | Yes | |
| 16 | MailingService | sendValidationInfluencerEmail | ValidationInfluencerEmail | Yes | |
| 17 | MailingService | sendRejectedRegistationInfluencerEmail | RejectedRegistationInfluencerEmail | Yes | |
| 18 | MailingService | sendResetCodePasswordEmail | ResetCodePasswordEmail | Yes | |
| 19 | MailingService | (verify email) | VerifyEmail (Mail) | Yes | Via SendEmailJob |
| 20 | MailingService | sendNewColabNomadeEmail | NewColabNomadeEmail | Yes | |
| 21 | MailingService | sendNewColabInfluencerEmail | NewColabInfluencerEmail | Yes | |
| 22 | MailingService | sendAcceptedColabCompanyEmail | AcceptedColabCompanyEmail | Yes | |
| 23 | MailingService | sendAcceptedColabInfluencerEmail | AcceptedColabInfluencerEmail | Yes | |
| 24 | MailingService | sendClientAcceptedColabCompanyEmail | ClientAcceptedColabCompanyEmail | Yes | |
| 25 | MailingService | sendRejectedColabInfluencerEmail | RejectedColabInfluencerEmail | Yes | |
| 26 | MailingService | sendRejectedColabNomadeEmail | RejectedColabNomadeEmail | Yes | |
| 27 | MailingService | sendClientRejectedColabCompanyEmail | ClientRejectedColabCompanyEmail | Yes | |
| 28 | MailingService | sendNewChatCompanyEmail | NewChatCompanyEmail | Yes | |
| 29 | MailingService | sendNewChatInfluencerEmail | NewChatInfluencerEmail | Yes | |
| 30 | MailingService | sendPublishedContentCompanyEmail | PublishedContentCompanyEmail | Yes | |
| 31 | MailingService | sendPublishedContentInfluencerEmail | PublishedContentInfluencerEmail | Yes | |
| 32 | MailingService | sendPublishedContentInternalEmail | PublishedContentInternalEmail | Yes | |
| 33 | MailingService | sendProductSentInfluencerEmail | ProductSentInfluencerEmail | Yes | |
| 34 | MailingService | sendProductSentCompanyEmail | ProductSentCompanyEmail | Yes | |
| 35 | MailingService | sendProductSentReminder1CompanyEmail | ProductSentReminder1CompanyEmail | Yes | |
| 36 | MailingService | sendProductSentReminder2CompanyEmail | ProductSentReminder2CompanyEmail | Yes | |
| 37 | MailingService | sendProductSentReminder2InternalEmail | ProductSentReminder2InternalEmail | Yes | |
| 38 | MailingService | sendIncidentSizeCompanyEmail | IncidentSizeCompanyEmail | Yes | |
| 39 | MailingService | sendIncidentSizeInfluencerEmail | IncidentSizeInfluencerEmail | Yes | |
| 40 | MailingService | sendIncidentSizeInternalEmail | IncidentSizeInternalEmail | Yes | |
| 41 | MailingService | sendIncidentWrongProductCompanyEmail | IncidentWrongProductCompanyEmail | Yes | |
| 42 | MailingService | sendIncidentWrongProductInfluencerEmail | IncidentWrongProductInfluencerEmail | Yes | |
| 43 | MailingService | sendIncidentWrongProductInternalEmail | IncidentWrongProductInternalEmail | Yes | |
| 44 | MailingService | sendIncidentOtherCompanyEmail | IncidentOtherCompanyEmail | Yes | |
| 45 | MailingService | sendIncidentOtherInfluencerEmail | IncidentOtherInfluencerEmail | Yes | |
| 46 | MailingService | sendIncidentOtherInternalEmail | IncidentOtherInternalEmail | Yes | |
| 47 | MailingService | sendProductReceivedCompanyEmail | ProductReceivedCompanyEmail | Yes | |
| 48 | MailingService | sendProductReceivedInfluencerEmail | ProductReceivedInfluencerEmail | Yes | |
| 49 | MailingService | sendReminder1ProductReceivedInfluencerEmail | Reminder1ProductReceivedInfluencerEmail | Yes | |
| 50 | MailingService | sendInfluencerModificationRequestInfluencerEmail | InfluencerModificationRequestInfluencerEmail | Yes | |
| 51 | MailingService | sendInfluencerModificationReminderCompanyEmail | InfluencerModificationReminderCompanyEmail | Yes | |
| 52 | MailingService | sendInfluencerModificationReminder2CompanyEmail | InfluencerModificationReminder2CompanyEmail | Yes | |
| 53 | MailingService | sendAutoCanceledModificationCompanyEmail | AutoCanceledModificationCompanyEmail | Yes | |
| 54 | MailingService | sendAutoCanceledModificationInfluencerEmail | AutoCanceledModificationInfluencerEmail | Yes | |
| 55 | MailingService | sendAutoCanceledModificationInternalEmail | AutoCanceledModificationInternalEmail | Yes | |
| 56 | MailingService | sendContactEmail | ContactEmail | Yes | |
| 57 | MailingService | sendConfirmedModificationRequestCompanyEmail | ConfirmedModificationRequestCompanyEmail | Yes | |
| 58 | MailingService | sendConfirmedModificationRequestInfluencerEmail | ConfirmedModificationRequestInfluencerEmail | Yes | |
| 59 | MailingService | sendConfirmedModificationCompanyEmail | ConfirmedModificationCompanyEmail | Yes | |
| 60 | MailingService | sendConfirmedModificationInfluencerEmail | ConfirmedModificationInfluencerEmail | Yes | |
| 61 | MailingService | sendClientCanceledModificationCompanyEmail | ClientCanceledModificationCompanyEmail | Yes | |
| 62 | MailingService | sendClientCanceledModificationInfluencerEmail | ClientCanceledModificationInfluencerEmail | Yes | |
| 63 | MailingService | sendClientCanceledModificationInternalEmail | ClientCanceledModificationInternalEmail | Yes | |
| 64 | MailingService | sendClientCanceledAcceptedCompanyEmail | ClientCanceledAcceptedCompanyEmail | Yes | |
| 65 | MailingService | sendClientCanceledAcceptedInfluencerEmail | ClientCanceledAcceptedInfluencerEmail | Yes | |
| 66 | MailingService | sendClientCanceledAcceptedInternalEmail | ClientCanceledAcceptedInternalEmail | Yes | |
| 67 | MailingService | sendAutoCanceledColabNomadeEmail | AutoCanceledColabNomadeEmail | Yes | |
| 68 | MailingService | sendClientCanceledPendingCompanyEmail | ClientCanceledPendingCompanyEmail | Yes | |
| 69 | MailingService | sendClientCanceledPendingInfluencerEmail | ClientCanceledPendingInfluencerEmail | Yes | |
| 70 | MailingService | sendClientCanceledPendingInternalEmail | ClientCanceledPendingInternalEmail | Yes | |
| 71 | MailingService | sendInfluencerCanceledPendingEmail | InfluencerCanceledPendingEmail | Yes | |
| 72 | MailingService | sendInfluencerCanceledPendingInternalEmail | InfluencerCanceledPendingInternalEmail | Yes | |
| 73 | MailingService | sendInfluencerCanceledPendingNomadeEmail | InfluencerCanceledPendingNomadeEmail | Yes | |
| 74 | MailingService | sendInfluencerCanceledPendingNomadeInternalEmail | InfluencerCanceledPendingNomadeInternalEmail | Yes | |
| 75 | MailingService | sendFavouriteOfferUpdateInfluencerEmail | FavouriteOfferUpdateInfluencerEmail | Yes | |
| 76 | MailingService | sendFeedbackEmail | FeedbackEmail | Yes | |
| 77 | MailingService | sendNomadeCanceledPendingInfluencerEmail | NomadeCanceledPendingInfluencerEmail | Yes | |
| 78 | MailingService | sendNomadeCanceledPendingClientInfluencerEmail | NomadeCanceledPendingClientInfluencerEmail | Yes | |
| 79 | MailingService | sendNomadeCanceledPendingClientCompanyEmail | NomadeCanceledPendingClientCompanyEmail | Yes | |
| 80 | MailingService | sendReminder1PublishContentCalendarEmail | Reminder1PublishContentCalendarEmail | Yes | |
| 81 | MailingService | sendReminder1PublishContentNoCalendarEmail | Reminder1PublishContentNoCalendarEmail | Yes | |
| 82 | MailingService | sendReminder2PublishContentCalendarEmail | Reminder2PublishContentCalendarEmail | Yes | |
| 83 | MailingService | sendReminder2PublishContentNoCalendarEmail | Reminder2PublishContentNoCalendarEmail | Yes | |
| 84 | MailingService | sendReminder3PublishContentCalendarEmail | Reminder3PublishContentCalendarEmail | Yes | |
| 85 | MailingService | sendReminder3PublishContentNoCalendarEmail | Reminder3PublishContentNoCalendarEmail | Yes | |
| 86 | MailingService | sendReminder4PublishContentNoCalendarEmail | Reminder4PublishContentNoCalendarEmail | Yes | |

Scheduled (sendEmailAt — delayed job): ReminderReceivedInfluencerEmail, ReminderPublishInfluencerEmail, ReminderReserveInfluencerEmail, 24h/2h/1h calendar reminders, ReminderReserveCompanyEmail, ProductSentReminder1/2, PendingColabReminder1/2, PendingBrandCollabReminder1/2/3, PendingColabModificationReminder, InfluencerModificationReminder(2), Reminder1ProductReceivedInfluencerEmail, ProductSentReminder2InternalEmail.

| Other | File | Method | Mailable | Queued? |
|-------|------|--------|----------|---------|
| Job | SendCollabReminder | handle | CollabReminder | Job queued, then Mail::send |

---

## 2. List of All Notifications

Notifications are **in-app + FCM**: `NotificationService` builds title/body/data, calls `messagingService->sendNotificationToUser($FCMToken, ...)` and `$this->create([...])` to store the in-app notification.

### 2.1 NotificationService methods (FCM + DB create)

| Method | Trigger context (see Section 3) |
|--------|----------------------------------|
| sendChatMessageSentNotification | Chat message sent (ChatController) |
| sendNewColabInfluencerNotification | New colab (HandlesHistoryTransitions PENDING_COMPANY; ColabController) |
| sendNewColabCompanyNotification | New colab (HandlesHistoryTransitions PENDING_COMPANY) |
| sendRejectedColabInfluencerNotification | Colab rejected |
| sendCanceledColabInfluencerNotification | Colab canceled |
| sendInfluencerCanceledPendingCompanyNotification | Influencer canceled pending |
| sendAutoCanceledColabCompanyNotification | Auto canceled |
| sendAcceptedColabInfluencerNotification | Colab accepted |
| sendClientAcceptedColabCompanyNotification | Client accepted colab |
| sendNewChatCompanyNotification | New chat (company) |
| sendNewChatInfluencerNotification | New chat (influencer) |
| sendPublishReminderInfluencerNotification | Publish reminder |
| sendPublishedContentCompanyNotification | Content published |
| sendProductSentInfluencerNotification | Product sent |
| sendProductSentCompanyNotification | Product sent (company) |
| sendConfirmedModificationCompanyNotification | Modification confirmed |
| sendConfirmedModificationInfluencerNotification | Modification confirmed (influencer) |
| sendIncidentSizeCompanyNotification | Incident (size) |
| sendIncidentSizeInfluencerNotification | Incident (size) |
| sendIncidentWrongProductCompanyNotification | Incident (wrong product) |
| sendIncidentWrongProductInfluencerNotification | Incident (wrong product) |
| sendIncidentOtherCompanyNotification | Incident (other) |
| sendIncidentOtherInfluencerNotification | Incident (other) |
| sendProductReceivedCompanyNotification | Product received |
| sendProductReceivedInfluencerNotification | Product received |
| sendReminder1ProductReceivedInfluencerNotification | Reminder 1 product received |
| sendInfluencerModificationReminderCompanyNotification | Modification reminder |
| sendInfluencerModificationReminder2CompanyNotification | Modification reminder 2 |
| sendAutoCanceledModificationCompanyNotification | Auto canceled modification |
| sendAutoCanceledModificationInfluencerNotification | Auto canceled modification (influencer) |
| sendInfluencerModificationRequestInfluencerNotification | Modification request |
| sendConfirmedModificationRequestCompanyNotification | Modification request confirmed |
| sendConfirmedModificationRequestInfluencerNotification | Modification request confirmed (influencer) |
| sendFavouriteOfferUpdateInfluencerNotification | Favourite offer update |
| sendCanceledColabCompanyNotification | Colab canceled (company) |
| sendAutoCanceledColabInfluencerNotification | Auto canceled (influencer) |
| sendReservationReminderInfluencerNotification | Reservation reminder |
| sendReservationReminderCompanyNotification | Reservation reminder (company) |
| sendPendingColabReminder1/2CompanyNotification | Pending colab reminder |
| sendPendingBrandCollabReminder1/2/3CompanyNotification | Pending brand reminder |
| sendPendingColabModificationReminderCompanyNotification | Pending modification reminder |
| sendClientCanceledModificationCompanyNotification | Client canceled modification |
| sendClientCanceledModificationInfluencerNotification | Client canceled modification (influencer) |
| sendClientCanceledAcceptedCompanyNotification | Client canceled accepted |
| sendClientCanceledAcceptedInfluencerNotification | Client canceled accepted (influencer) |
| sendClientCanceledPendingCompanyNotification | Client canceled pending |
| sendClientCanceledPendingInfluencerNotification | Client canceled pending (influencer) |
| sendInfluencerCanceledPendingNotification | Influencer canceled pending |
| sendInfluencerCanceledPendingNomadeNotification | Influencer canceled pending (Nomade) |
| sendNomadeCanceledPendingInfluencerNotification | Nomade canceled pending |
| sendNomadeCanceledPendingClientInfluencerNotification | Nomade canceled (influencer) |
| sendNomadeCanceledPendingClientCompanyNotification | Nomade canceled (company) |
| sendReminder1/2/3/4PublishContent*Notification | Publish content reminders |
| send24HoursCalendarReminderNotification | 24h calendar reminder |
| send2HoursCalendarReminderNotification | 2h calendar reminder |
| send1HourCalendarReminderCompanyNotification | 1h calendar reminder (company) |
| schedule* (various) | Same as above but scheduled (SendScheduledNotificationJob) |

### 2.2 Laravel notification (email channel)

| Notification | File | Trigger |
|--------------|------|--------|
| VerifyEmailNotification | app/Notifications/VerifyEmailNotification.php | User registration: `Registered` → `SendEmailVerificationNotification` → `$user->notify(new VerifyEmailNotification)`. Uses `toMail()`; MailingService::sendVerificationEmail dispatches `VerifyEmail` mailable via SendEmailJob. |

---

## 3. Trigger Flows

### 3.1 Main flow: state change → email/notification

```
ColabController::pushHistory (API)
  → ColabService::pushHistory
  → $model->collabable->attachHistory($state, $data)   [HasHistoryEvents]
  → event(HistoryAttachedEvent($this, $state, $data, Auth::user()))
  → HistoryNotifier::handle (QUEUED — ShouldQueue)
  → handleStateTransition($state, $colab, $event->user ?? Auth::user(), $data)
  → HandlesHistoryTransitions: handle*State($colab, $user)
  → mailingService()->send*() / notificationService()->send*()
  → SendEmailJob::dispatch() or sendEmailAt() / messagingService->sendNotificationToUser() + create()
```

- **Event:** `HistoryAttachedEvent` (model = collabable, state, data, user).
- **Listeners:** `HistoryNotifier` (queued), `HistoryLogger` (subscriber, sync).
- **User in queue:** `$event->user ?? Auth::user()` — when the job runs in the worker, `Auth::user()` is null, so `$user` can be null unless the event stored the actor.

### 3.2 Trigger matrix (state → emails/notifications)

| State / context | Emails (MailingService) | Notifications (NotificationService) |
|-----------------|-------------------------|-------------------------------------|
| PENDING_COMPANY | schedulePending* (reminders), NewColabNomade, NewColabInfluencer | sendNewColabCompany, sendNewColabInfluencer, schedule* reminders |
| ACCEPTED | AcceptedColab*, ClientAccepted*, schedule 24h/2h/1h, scheduleProductSent*, etc. | sendAcceptedColabInfluencer, sendClientAcceptedColabCompany, schedule* |
| REJECTED | RejectedColabInfluencer, RejectedColabNomade, ClientRejectedColabCompany | sendRejectedColabInfluencer |
| MODIFICATION_IN_PROGRESS | ConfirmedModificationRequest*, NewChat*, scheduleInfluencerModificationReminder* | sendConfirmedModificationRequest*, sendNewChat*, schedule* |
| FINISHED | PublishedContent*, ProductSent*, etc. | sendPublishedContent*, sendProductSent*, etc. |
| SENT | ProductReceived*, scheduleReminder1ProductReceived* | sendProductReceived*, schedule* |
| INCIDENT | IncidentSize*, IncidentWrongProduct*, IncidentOther* (company/influencer/internal) | sendIncidentSize*, sendIncidentWrongProduct*, sendIncidentOther* |
| CANCELED (handleCancelledState) | ClientCanceled*, InfluencerCanceled*, NomadeCanceled*, AutoCanceled*, CanceledColab* | sendClientCanceled*, sendInfluencerCanceled*, sendNomadeCanceled*, sendAutoCanceled*, sendCanceledColab* |

### 3.3 Direct callers (no event)

| Caller | Method | Emails / notifications |
|--------|--------|-------------------------|
| ColabController | after creating colab | sendNewColabInfluencerEmail, sendNewColabNomadeEmail, sendNewColabInfluencerNotification |
| ColabService | after update (date/data change, modification request) | sendDateChanged*, sendDataChanged*, sendInfluencerModificationRequestInfluencerEmail; sendDateChangedColabNotifications, sendDataChangedColabNotifications |
| CompanyController | company registration/validation | sendRegistrationCompanyAdminEmail, sendCompanyRegistrationOnboardingEmail, sendValidationCompanyEmail |
| LeadController | lead registration | sendLeadRegistrationLinkEmail, sendRegistrationRequestCompany, sendRegistrationRequestNomade |
| AuthController | password reset | sendResetCodePasswordEmail |
| InfluencerController | influencer registration | sendRegistrationInfluencerEmail, sendRegistrationInfluencerAdminEmail |
| UserController | validation, contact | sendValidationInfluencerEmail, sendValidationCompanyEmail, sendContactEmail |
| OfferController | favourite update | sendFavouriteOfferUpdateInfluencerEmail, sendFavouriteOfferUpdateInfluencerNotification |
| FeedbackController | feedback | sendFeedbackEmail |
| ChatController | chat message | sendChatMessageSentNotification |
| SendPublishContentReminders (console) | scheduled | sendReminder*PublishContent*Email, sendReminder*PublishContent*Notification |
| CancelExpiredCollaborations (console) | pushHistory (no user) | Same as state CANCELED (user can be null) |

---

## 4. Queue vs Sync Execution

### 4.1 Queued

| Component | Implements ShouldQueue | Runs in queue |
|-----------|------------------------|---------------|
| HistoryNotifier | Yes | Yes — listener runs in worker |
| SendEmailJob | Yes | Yes — all MailingService emails go through it |
| SendScheduledNotificationJob | Yes | Yes — scheduled notifications |
| SendCollabReminder | Yes | Yes |
| VerifyEmailNotification | Yes | Yes — notification queued when sent |

### 4.2 Synchronous

| Component | Runs sync |
|-----------|-----------|
| MailingService | Sync — methods run in request/worker; they only dispatch SendEmailJob (or schedule it) |
| NotificationService | Sync — methods run in request/worker; they call FCM + create() directly |
| HistoryLogger | Sync — subscriber, no ShouldQueue |
| HistoryValidator | Sync — listener for HistoryAttachingEvent |

### 4.3 Summary

- **Emails:** Always sent via a **queued** job (SendEmailJob or delayed SendEmailJob). The **call** to MailingService is sync (from controller, ColabService, or HistoryNotifier); the **send** happens when the job runs.
- **Notifications (FCM + DB):** Sent **synchronously** from NotificationService when the method is invoked (e.g. from HistoryNotifier in the worker).
- **HistoryNotifier:** **Queued** — so all state-driven emails/notifications are triggered from a queue worker. `$event->user` can be null in the worker; HandlesHistoryTransitions now guards/null-checks `$user` where used.

---

## 5. Identified Risks Per Item

### 5.1 Null / missing data risks

| Location | Risk | Severity |
|----------|------|----------|
| HandlesHistoryTransitions | `$user` null when HistoryNotifier runs from queue (event stored actor; if not serialized or Auth::user() null). | Mitigated by recent guards/early returns. |
| HandlesHistoryTransitions | `$colab->influencer->getUser()` / `$colab->offer->company->getUser()` passed to mailing methods — if relation missing or getUser() null, downstream code can fail. | Medium — some paths use InfluencerHelper; company user still raw in places. |
| NotificationService | Many uses of `$colab->offer->company->users->first()->uid` or `company->getUser()->id` without null-safe helper (company user equivalent of InfluencerHelper). | Medium — company can have no users or getUser() null. |
| MailingService | `$to = InfluencerHelper::getEmail($colab->influencer, '')` — empty string as recipient can cause silent skip or mail driver error. | Low–medium. |
| SendEmailJob | When `collabId`/`expectedStateId` set, job loads Colab; if relations (e.g. influencer, company) not loaded, Mailable constructor may access null. | Medium for mailables that expect loaded relations. |
| ColabController | sendNewColab* called right after colab create — relations may not be loaded if not eager-loaded. | Low if Colab is fresh with relations. |

### 5.2 Carbon / type risks

| Location | Risk | Status |
|----------|------|--------|
| VerifyEmailNotification | addMinutes(config('auth.verification.expire')) | Cast to (int) in code. |
| UpdateColabRequest / StoreColabRequest / OfferResource | addMinutes($advanceNotificationTime) | Cast to (int) in recent fix. |
| MailingService::sendEmailAt | Carbon::now()->diffInSeconds($sendTime) | Numeric; low risk. |

### 5.3 Try/catch and logging

| Location | Try/catch | Logging |
|----------|-----------|---------|
| SendEmailJob::handle | Yes — catch around Mail::send, logs to email_errors | Logs success to email channel |
| NotificationService send* | No try/catch around sendNotificationToUser or create() | No consistent log before send |
| MailingService send* | No try/catch (only dispatch) | No per-send log (only in job) |
| HandlesHistoryTransitions | try/catch in handleCancelledState for task cancellation; not around each send | Logs in places (e.g. CONFIRM_FLOW, HANDLE_CANCELLED_STATE) |

Failure in NotificationService (e.g. FCM or DB) can throw and bubble (e.g. from HistoryNotifier job) and may leave partial state (e.g. email sent but notification failed).

### 5.4 Data dependencies (required data)

| Type | Typical required data | Source | Risk |
|------|------------------------|--------|------|
| Colab emails | colab, influencer, offer, company, recipient user (email) | $colab from event/model; recipient from InfluencerHelper or company->getUser() | influencer/company/user null in queue if not loaded or deleted |
| Colab notifications | colab, FCM token (uid), receiver_id (user id) | Same as above | getFCMToken(null) may return null or throw depending on implementation |
| Lead/registration emails | lead, url, company, user | Controller / request | Lower — request-scoped |
| Verification email | User, verification URL | Auth + config | Config expire string risk mitigated by (int) cast |

---

## 6. Critical Issues (High Probability of Failure)

1. **Company user access in NotificationService**  
   Many methods use `$colab->offer->company->users->first()->uid` or `company->getUser()->id` without null-safe helper. If company has no users or getUser() returns null, this will throw. **Recommendation:** Introduce a CompanyHelper (or equivalent) similar to InfluencerHelper and use it for all company user access.

2. **HandlesHistoryTransitions passing raw getUser()**  
   Some calls still pass `$colab->influencer->getUser()` or `$colab->offer->company->getUser()` into MailingService. When the listener runs in the queue, relations may be serialized/restored; if influencer or company user is missing, this can be null. **Recommendation:** Use InfluencerHelper (and a company-user helper) and avoid passing raw User when the chain can be null.

3. **No try/catch in NotificationService**  
   If `sendNotificationToUser` or `create()` fails, the exception propagates and can fail the entire HistoryNotifier job (and possibly retries). **Recommendation:** Consider try/catch per notification with log and optional rethrow so one failed notification does not abort the rest.

4. **Empty email recipient**  
   MailingService uses `InfluencerHelper::getEmail($influencer, '')` in many places. Sending to `''` may be invalid. **Recommendation:** Skip dispatch or log when `$to === ''` and optionally alert.

5. **Scheduled jobs and state validation**  
   SendEmailJob with `expectedStateId` skips send if colab state changed; this is correct. Scheduled notification job (SendScheduledNotificationJob) behavior should be confirmed (e.g. same state check) to avoid sending outdated reminders.

6. **VerifyEmailNotification**  
   Queued; uses config for expire (cast to int). If config key is missing or wrong type elsewhere, similar Carbon issues could appear. Only one usage found with (int) cast.

---

## 7. Summary Tables

### 7.1 All mailables (by name, for reference)

- AcceptedColabCompanyEmail, AcceptedColabInfluencerEmail  
- AutoCanceledColabCompanyEmail, AutoCanceledColabInfluencerEmail, AutoCanceledColabNomadeEmail  
- AutoCanceledModificationCompanyEmail, AutoCanceledModificationInfluencerEmail, AutoCanceledModificationInternalEmail  
- CanceledColabEmail  
- ClientAcceptedColabCompanyEmail  
- ClientCanceledAcceptedCompanyEmail, ClientCanceledAcceptedInfluencerEmail, ClientCanceledAcceptedInternalEmail  
- ClientCanceledModificationCompanyEmail, ClientCanceledModificationInfluencerEmail, ClientCanceledModificationInternalEmail  
- ClientCanceledPendingCompanyEmail, ClientCanceledPendingInfluencerEmail, ClientCanceledPendingInternalEmail  
- ClientRejectedColabCompanyEmail  
- CompanyRegistrationOnboardingEmail, CompanyRegistrationReceivedEmail  
- ConfirmedModificationCompanyEmail, ConfirmedModificationInfluencerEmail  
- ConfirmedModificationRequestCompanyEmail, ConfirmedModificationRequestInfluencerEmail  
- ContactEmail  
- DataChangedColabEmail, DateChangedColabEmail  
- FeedbackEmail  
- GenericEmail  
- InfluencerCanceledPendingCompanyEmail, InfluencerCanceledPendingEmail, InfluencerCanceledPendingInternalEmail, InfluencerCanceledPendingNomadeEmail, InfluencerCanceledPendingNomadeInternalEmail  
- InfluencerModificationReminderCompanyEmail, InfluencerModificationReminder2CompanyEmail  
- InfluencerModificationRequestInfluencerEmail  
- LeadRegistrationLinkEmail  
- NewChatCompanyEmail, NewChatInfluencerEmail  
- NewColabInfluencerEmail, NewColabNomadeEmail  
- NomadeCanceledPendingClientCompanyEmail, NomadeCanceledPendingClientInfluencerEmail, NomadeCanceledPendingInfluencerEmail  
- PendingBrandCollabReminder1/2/3CompanyEmail  
- PendingColabReminder1/2CompanyEmail  
- PendingColabModificationReminderCompanyEmail  
- ProductReceivedCompanyEmail, ProductReceivedInfluencerEmail  
- ProductSentCompanyEmail, ProductSentInfluencerEmail  
- ProductSentReminder1/2CompanyEmail, ProductSentReminder2InternalEmail  
- PublishedContentCompanyEmail, PublishedContentInfluencerEmail, PublishedContentInternalEmail  
- RegistrationInfluencerEmail, RegistrationInfluencerAdminEmail  
- RegistrationRequestCompanyEmail, RegistrationRequestNomadeEmail  
- RegistrationCompanyAdminEmail  
- RejectedColabInfluencerEmail, RejectedColabNomadeEmail  
- RejectedRegistationInfluencerEmail  
- Reminder1ProductReceivedInfluencerEmail  
- Reminder1/2/3/4PublishContentCalendar/NoCalendarEmail  
- ReminderReceivedInfluencerEmail, ReminderPublishInfluencerEmail, ReminderReserveInfluencerEmail, ReminderReserveCompanyEmail  
- Reminder24HoursCalendarInfluencerEmail, Reminder2HoursCalendarInfluencerEmail, Reminder1HourCalendarCompanyEmail  
- ResetCodePasswordEmail  
- ValidationCompanyEmail, ValidationInfluencerEmail  
- VerifyEmail (Mail)  
- FavouriteOfferUpdateInfluencerEmail  
- IncidentSize/IncidentWrongProduct/IncidentOther (Company/Influencer/Internal) emails  

### 7.2 Mailables implementing ShouldQueue (run as queued when sent via Laravel)

- ClientCanceledPendingCompanyEmail, ClientCanceledPendingInfluencerEmail, ClientCanceledPendingInternalEmail  
- InfluencerCanceledPendingEmail, InfluencerCanceledPendingInternalEmail, InfluencerCanceledPendingNomadeEmail, InfluencerCanceledPendingNomadeInternalEmail  

All other mailables are sent via **SendEmailJob** (which is queued), so the **job** is queued; the mailable itself does not need to implement ShouldQueue for queueing. The ones above that implement ShouldQueue may be used in contexts where they are sent directly (e.g. Mail::send in a job) and would then be serialized and run in the queue.

---

**End of audit. No code changes were made.**
