Email workspaceInsurance policy closed
Source: App\Mail\InsurancePolicyClosed, app/Mail/InsurancePolicyClosed.php; resources/views/emails/insurance/policy_closed.blade.php using mail::message. Subject: Your insurance policy has been closed! (unchanged). Suggested preheader: Insurance policy {{policy_id}} has been closed.
| Token | Existing source | Handling |
|---|---|---|
| {{first_name}} | user.first_name ?? '', user = policy.user | Optional escaped greeting; remove the whole marked block if empty/null, without inventing a name. |
| {{policy_id}} | policy.policy_id | Required original display. There is no N/A fallback in this source; preserve supplied empty/0 rather than copying the order-completed fallback. |
| {{closure_reason}} | policy.closure_reason | Required escaped multiline row, unconditional even if supplied empty/0. Do not replace it with a generic cancellation/expiry reason. |
| {{app_name}} | config('app.name') | Original Thanks sign-off. |
| {{preheader}} | Source policy-ID/closure summary above | Escaped text; no refund, coverage, claim or processing-time promise. |
No other optional business sections, workflow URLs, attachments/PDFs or legacy duplicate link fields exist. Default Built logo/optional {{logo_url}} and actual CID/approved HTTPS shared assets follow [shared-footer](../shared-footer.md). Missing mandatory policy/reason/user routing context needs an explicit consuming-project decision, not a guessed identifier or recipient.
Trigger and native behavior
InsurancePolicyController::close (:65–115), route POST insurance-policies/{id}/close (routes/api.php:225), loads policy/user/order, checks source policy.status == open and validates a required reason. A transaction sets closed_at and closure_reason and creates NewInsuranceActivity; afterward it queues this Mailable to policy.user.email with native internal BCC at :97. A separate SMS uses the order phone. Preserve closure/recipient/error ordering and do not alter SMS, policy/claim records or coverage dates through HTML.
NewInsurancePolicy derives status from closed_at/end_at: closed when closed_at exists, otherwise open/expired by date. The email uses that source close operation; it does not calculate status or invent an expiry email. build() binds policy/user. There is no explicit From/Reply-To override. Keep configured headers, subject, To/BCC and queue()/transport policy. Stored policy documents are not email attachments here.
Closure is not automatically cancellation, a refund, a claim decision or payout. The draft states only the existing policy ID and closure reason. Four fictional states cover normal closure, reason 0, supplied empty values as a view-only robustness check and long fields. Normal close validation still requires reason; the fixture does not create a valid empty-reason closure.
Integration and safety
Use the existing Mailable with a complete HTML Blade view and proposed plain-text alternative. Portable {{placeholder_name}} tokens are not automatically Blade variables. Escape names, policy IDs, reason text/attributes and validate image URLs separately; use one pass for string replacement. Preserve the optional-greeting and unconditional-reason distinction. No new framework or financial/insurance behavior is needed.
Files: public/ready/insurance-policy-closed.html/.txt and previews/insurance-policy-closed-*.html/.txt. No separate per-email project-owned text source found. No real policy/closure/message was created or sent. Browser checks do not verify actual policy/claim/coverage results, Gmail/Outlook/iOS Mail/CID behavior or production delivery.