BuiltEmail workspace

Virtual card created

Source: App\Mail\CardCreatedNotification, app/Mail/CardCreatedNotification.php; resources/views/emails/CardCreated.blade.php. Caller: MapleradController::handleWebhook, app/Http/Controllers/Wallet/MapleradController.php:142.

Existing effective subject: Card Created Notification. No explicit subject override exists; Laravel derives it from the class basename. Preserve native behavior rather than adding a new subject silently. Suggested preheader: Your virtual card {{masked_card_number}} was created.

Required portable values

TokenExisting sourceHandling
{{card_name}}card.nameEscaped holder/display name.
{{masked_card_number}}card.card_pan_maskedOnly original masked display; never card_pan, CVV or expiry. Keep as text.
{{card_type}}card.card_typeEscaped original issuer/type value; no inferred card brand.
{{card_balance}}card.amountRaw existing display, preserving zero. Original has no currency prefix or number_format; do not silently introduce one or divide by 100.
{{login_url}}Fixed https://app.built.africaValidated original app-root URL, escaped attribute. Log in to Built does not directly classify an expense.
{{preheader}}Suggested masked-card summary aboveEscaped text; no unmasked account information.

Default logo/optional {{logo_url}} and actual CID/approved HTTPS footer images follow [shared-footer](../shared-footer.md). No optional body section or legacy duplicate link exists.

Trigger and native behavior

handleWebhook's issuing.created.successful branch fetches the remote card, updates the local card, handles an existing funding service, reloads the real card and calls Mail::to(wallet-user.email)->send(new CardCreatedNotification(realCard)). Preserve that order, remote/local ID resolution and existing error handling. Push notification is a separate channel; it is not another email. Do not infer that creation guarantees card balance/funding or every active-status property.

The body prints only name, masked number, card type and raw balance, followed by the original instruction to log in and classify the expense in the books. The model contains more sensitive card fields; none belongs in this redesign's contract or fixtures. The provider balance/amount units should be reviewed as an explicit target-project decision if behavior is changed later.

There is no explicit From/Reply-To override, PDF or business attachment. Preserve configured headers, native subject and send(); Queueable is not proof of queued sending. Underlying transport/outbox may still be asynchronous. API owns the body even though a provider webhook triggers it.

Integration and safety

Use the existing Mailable with a complete HTML Blade view and a proposed text alternative. Portable {{placeholder_name}} tokens are not automatically Blade variables. Escape values/attributes, validate approved app-root and image URLs separately, and use one pass for replacements. Never read/fetch live card data to populate previews. No expiry, PAN, CVV, activation, funding, dispute or reversal action is added.

Three fictional states cover created, zero balance and long masked/display values. Files: public/ready/virtual-card-created.html/.txt and previews/virtual-card-created-*.html/.txt. No separate project-owned plain-text alternative was found. Browser checks do not verify actual card/provider behavior, Gmail/Outlook/iOS Mail, CID delivery or production delivery.