SOP-LC-01: Lifecycle Communications¶
Version: 1.5 Status: Production Ready. All campaigns built in Customer.io (inactive, pending launch activation). All infrastructure wired and end-to-end tested. Created: January 2026 Updated: April 2026 Owner: Protocol Raw Operations Architecture Owner: Anton (Founder) Copy Owner: Protocol Raw Operations (Head of Growth) Related: Email Design System v1.3, SOP-CS-00, SOP-REF-01, SOP-SUB-00, SOP-DLV-01, SOP-EMAIL-01, SOP-MON-01
Key Changes in v1.5¶
- Transition Protection sequence added: 5 automated emails (T1-T4, T6) replacing the old Week 1 / Week 2 onboarding check-ins
- T1 Arrival Guide (Day 0, Utility): freezer storage, thawing, first meal instructions
- T2 Day 2 Check-in (Day 2, Relational): appetite and acceptance check-in, reply-inviting
- T3 Day 4 Normalisation (Day 4, Relational): soft stools and meal hesitation normalisation
- T4 Day 7 Milestone (Day 7, Relational): transition complete, reply-inviting, core Golden Path metric
- T6 Pre-Box-2 Confidence (~3 days before Box 2 charge, Utility): cadence-aware, routine confidence reinforcement
- Founder check-in (T5, Day 10) unchanged
- Old "Onboarding Check-ins" campaign retired in Customer.io (renamed [RETIRED], left inactive)
- New Customer.io campaigns: "Transition Protection" (trigger: first_box_delivered) and "Pre-Box-2 Confidence" (trigger: pre_box2_confidence)
- Pre-Box-2 event created by extended create_first_delivery_event with scheduled_for = next_billing_date - 3 days
- New function fn_reschedule_pre_box2_event_v1 re-schedules or removes pre_box2_confidence event on subscription changes
- Golden Path metrics remapped in Growth Playbook v2.4
1. Overview¶
Purpose¶
This SOP defines all automated customer email communications for Protocol Raw. It consolidates copy, triggers, timing, and technical implementation details into a single reference document.
Philosophy¶
Emails are not marketing brochures. They are direct communication with someone who already trusts us. Every email should feel like a note from a competent, thoughtful company, not a sales pitch.
Core principles:
- Calm, expert, reassuring
- Short, declarative, plain English
- Factual without being cold
- Respectful of time
We are: Confident, warm, systematic We are NOT: Salesy, cute, desperate for attention
Email Categories¶
| Category | Purpose | Headline | Sign-off | Tone |
|---|---|---|---|---|
| Transactional | Receipts, confirmations, system updates | Optional | Protocol Raw | Factual, simple |
| Operational | Delivery reminders, billing notices | Optional | Protocol Raw | Factual, simple |
| Lifecycle | Onboarding, win-back, reactivation | Yes | Protocol Raw | Warm, affirming, one clear purpose |
| Lifecycle (Relational) | Check-ins, reply-inviting | No | Persona | Conversational, human |
| Support | Replies to customer inquiries | No | Persona | Conversational, human |
Personas¶
For relational emails that invite replies, we use named personas:
| Persona | Usage |
|---|---|
| Sophie | Support, check-ins |
| Tom | Support, check-ins |
| Lucy | Support, check-ins |
Assignment: Deterministic per customer (hash of customer ID). Same customer always gets same persona across all touchpoints. Persona is set on the Customer.io profile via send-lifecycle-events identify call. Missing persona is a data integrity issue; do not mask with template defaults in production.
2. Email Inventory¶
Implementation Status Summary¶
| Category | Total | Live | Built (inactive) |
|---|---|---|---|
| Transactional (immediate) | 12 | 12 | 0 |
| Operational (scheduled) | 2 | 2 | 0 |
| Lifecycle (campaign) | 5 | 0 | 5 |
| Lifecycle relational (campaign) | 4 | 0 | 4 |
| Lifecycle founder (manual) | 1 | 0 | 1 |
| Win-back (campaign) | 2 | 0 | 2 |
| Abandonment | 2 | 2 | 0 |
| Total | 28 | 16 | 12 |
Master List¶
| # | Category | Trigger Event | Timing | CIO ID | Status | |
|---|---|---|---|---|---|---|
| 1a | Order Confirmation (First) | Transactional | trg_order_confirmation |
Immediate | 6 | Live |
| 1b | Order Confirmation (Repeat) | Transactional | trg_order_confirmation |
Immediate | 7 | Live |
| 2 | Transition Guide | Lifecycle | order_created_first |
+1 day | Campaign | Built (inactive) |
| 2a | Arrival Guide (T1) | Lifecycle | first_box_delivered |
+2 hours | Campaign | Built (inactive) |
| 2b | Day 2 Check-in (T2) | Lifecycle (Relational) | first_box_delivered |
+2 days | Campaign | Built (inactive) |
| 2c | Day 4 Normalisation (T3) | Lifecycle (Relational) | first_box_delivered |
+4 days | Campaign | Built (inactive) |
| 2d | Day 7 Milestone (T4) | Lifecycle (Relational) | first_box_delivered |
+7 days | Campaign | Built (inactive) |
| 2e | Pre-Box-2 Confidence (T6) | Lifecycle | pre_box2_confidence |
Cadence-aware (~Box 2 - 3 days) | Campaign | Built (inactive) |
| 3 | Founder Check-in (Day 10) | Lifecycle (Relational) | first_box_delivered |
+10 days | Phase A manual / Customer.io inactive | Phase A manual |
| 4 | ~~Week 2 Check-in~~ | ~~Lifecycle (Relational)~~ | ~~first_box_delivered~~ |
~~+14 days~~ | ~~Campaign~~ | Removed (Phase A). Founder email at day 10 replaces both Week 1 and Week 2. Re-evaluate for Phase B. |
| 5 | 7-Day Delivery Reminder | Operational | send-delivery-emails |
-7 days | Track event | Live |
| 6 | 48-Hour Lock | Operational | send-delivery-emails |
-48 hours | Track event | Live |
| 7 | Dispatch Confirmation | Transactional | trg_shipment_email |
On dispatch | 17 | Live |
| 8 | Delivery Confirmation | Transactional | trg_shipment_email |
On delivery | 18 | Live |
| 9 | Skip Confirmation | Transactional | trg_subscription_action_email |
Immediate | 9 | Live |
| 10 | Reschedule Confirmation | Transactional | trg_subscription_action_email |
Immediate | 10 | Live |
| 11 | Pause Confirmation | Transactional | trg_subscription_action_email |
Immediate | 11 | Live |
| 12 | Resume Confirmation | Transactional | trg_subscription_action_email |
Immediate | 12 | Live |
| 13 | Cancellation Confirmation | Transactional | trg_subscription_action_email |
Immediate | 13 | Live |
| 14 | Box Size Change | Transactional | trg_subscription_action_email |
Immediate | 14 | Live |
| 15 | Frequency Change | Transactional | trg_subscription_action_email |
Immediate | 15 | Live |
| 16 | Address Change | Transactional | trg_subscription_action_email |
Immediate | 16 | Live |
| 17 | Win-back 1 | Lifecycle | subscription_cancelled |
+30 days | Campaign | Built (inactive) |
| 18 | Win-back 2 | Lifecycle | subscription_cancelled |
+60 days | Campaign | Built (inactive) |
| A1 | Calculator Abandonment 1h | Lifecycle | send-abandonment-events |
+1 hour | Track event | Live |
| A2 | Calculator Abandonment 48h | Lifecycle | send-abandonment-events |
+48 hours | Track event | Live |
Emails Documented Elsewhere¶
| Source SOP | |
|---|---|
| Referral signup | SOP-REF-01 |
| Credit earned | SOP-REF-01 |
| Credit applied | SOP-REF-01 |
| Credit expiring | SOP-REF-01 |
| Card expiring (30 day) | SOP-SUB-00 |
| Card expiring (7 day) | SOP-SUB-00 |
| Payment failed (Day 0/3/7/10) | SOP-SUB-00 |
| Courier exceptions | SOP-DLV-01 |
3. Onboarding Sequence¶
3.1 Order Confirmation (First)¶
Customer.io ID: 6
Trigger: AFTER INSERT on raw_ops.orders (first order detected by trigger_order_confirmation)
Timing: Immediate
Category: Transactional
Subject: Your order is confirmed Preview text: Order #{{order.order_number}}. We'll have it with you soon.
Hi {{customer.first_name}},
We've received your order and it's being prepared.
Order #{{order.order_number}} {{order.box_size}} box, £{{order.total_price}} {{customer.address1}}, {{customer.city}}, {{customer.zip}}
Your box will be matched to a tested batch. It will arrive frozen within 3-5 working days.
You'll receive tracking as soon as it leaves our freezer. Once it arrives, scan the QR code on any pouch to view your batch report.
Tomorrow we'll send {% if dog_name %}{{dog_name}}'s{% else %}your{% endif %} personalised transition guide with a day-by-day feeding schedule.
Any questions, just reply.
Protocol Raw
Also triggers: create_order_created_event inserts order_created_first lifecycle event into lifecycle_events table. This event fires the Transition Guide campaign in Customer.io and syncs profile attributes (first_name, dog_name, persona, transition_url, subscription_status, box_size, timezone).
3.2 Order Confirmation (Repeat)¶
Customer.io ID: 7
Trigger: AFTER INSERT on raw_ops.orders (repeat order)
Timing: Immediate
Category: Transactional
Subject: Your order is confirmed Preview text: Order #{{order.order_number}}. We'll have it with you soon.
Hi {{customer.first_name}},
We've received your order and it's being prepared.
Order #{{order.order_number}} {{order.box_size}} box, £{{order.total_price}} {{customer.address1}}, {{customer.city}}, {{customer.zip}}
Your box will be matched to a tested batch. It will arrive frozen within 3-5 working days.
You'll receive tracking as soon as it leaves our freezer.
Any questions, just reply.
Protocol Raw
3.3 Transition Guide¶
Customer.io Campaign: Transition Guide
Trigger event: order_created_first (via lifecycle events pipeline)
Timing: +1 day after order
Category: Lifecycle
From: Protocol Raw / hello@protocolraw.co.uk
Subject: {% if dog_name %}{{dog_name}}'s{% else %}Your{% endif %} transition guide is ready Preview text: A 10-day schedule for switching to raw.
Your 10-day transition plan
Hi {{first_name}},
Your first box is on the way. You've got a clear plan for the switch.
A gradual change gives {% if dog_name %}{{dog_name}}{% else %}your dog{% endif %} time to adapt. The schedule shows the exact gram amounts for each day, based on the feeding plan you calculated.
[CTA: View your schedule -> {{transition_url}}]
Softer stools in the first few days can be normal. This usually settles within a week.
You may also notice {% if dog_name %}{{dog_name}}{% else %}your dog{% endif %} drinking less water. Raw food contains more moisture, so more of their hydration comes from the food itself.
Questions during the transition? Reply here and we'll help.
Protocol Raw
Notes: Single purpose: transition plan. No portal management, no skip/pause instructions. The transition_url is a pre-built deep link to the Customer Portal TransitionView (https://my.protocolraw.co.uk?view=transition). The portal handles authentication.
3.3a Transition Protection Sequence (T1-T4)¶
Customer.io Campaign: Transition Protection
Trigger event: first_box_delivered (via lifecycle events pipeline)
Category: Lifecycle (T1, T6: Utility; T2-T4: Relational)
From: Protocol Raw / hello@protocolraw.co.uk (T1, T6); {{persona}} at Protocol Raw / hello@protocolraw.co.uk (T2-T4)
Status: Built, inactive (pending launch activation)
Campaign workflow:
first_box_delivered
-> Wait 2 hours -> Send T1 (Arrival Guide)
-> Wait 2 days -> Branch: subscription_status = active? -> No: Exit / Yes: Send T2 (Day 2 Check-in)
-> Wait 2 days -> Branch: subscription_status = active? -> No: Exit / Yes: Send T3 (Day 4 Normalisation)
-> Wait 3 days -> Branch: subscription_status = active? -> No: Exit / Yes: Send T4 (Day 7 Milestone)
-> Exit
T1: Arrival Guide (Day 0, +2 hours) Layout: Utility
Subject: {% if customer.dog_name %}{{customer.dog_name}}'s{% else %}Your{% endif %} food is here Preview text: Freezer first, then start tomorrow.
{% if customer.dog_name %}{{customer.dog_name}}'s{% else %}Your{% endif %} food is here.
Move the trays to your freezer as soon as you can. The food arrives frozen and should be stored frozen until you're ready to use it.
To prepare a meal, move one tray to the fridge the night before. It will be thawed and ready to serve by morning.
Tomorrow is Day 1 of {% if customer.dog_name %}{{customer.dog_name}}'s{% else %}your{% endif %} transition. Your schedule has the exact amounts for each day.
[CTA: View your transition schedule -> {{transition_url}}]
Any questions, just reply.
Protocol Raw
Notes: One job: bridge from box arrival to first meal. No proof portal, no QR code, no handling hygiene beyond implicit "move to freezer." CTA links to the same transition_url used in the Transition Guide email. Data requirements: dog_name, transition_url (both synced via first_box_delivered identify call).
T2: Day 2 Check-in (Day 2) Layout: Relational
Subject: How's {% if customer.dog_name %}{{customer.dog_name}}{% else %}your dog{% endif %} finding the food? Preview text: Just checking in.
Hi {{first_name}},
{% if customer.dog_name %}{{customer.dog_name}}{% else %}Your dog{% endif %} should be a day or two into the transition now. I just wanted to check how it's going.
Some dogs take to raw food straight away. Others need a day or two to get used to a new texture and temperature.
If {% if customer.dog_name %}{{customer.dog_name}}{% else %}your dog{% endif %} ate it happily, that's great. A bit of hesitation at the bowl or extra sniffing before eating is completely fine too.
If it's going well, or anything feels off, just reply and let me know.
{{persona}} Protocol Raw
Notes: Core Golden Path metric (Steps 7-8: open rate, reply rate). First relational email in the sequence. Reply invitation covers both positive and negative responses for engagement measurement. Does not mention stools (that is Day 4's job). Data requirements: first_name, dog_name, persona.
T3: Day 4 Normalisation (Day 4) Layout: Relational
Subject: How's {% if customer.dog_name %}{{customer.dog_name}}{% else %}your dog{% endif %} settling in? Preview text: A few things that can happen around Day 4.
Hi {{first_name}},
By now {% if customer.dog_name %}{{customer.dog_name}}{% else %}your dog{% endif %} is partway through the transition. Around this point, a couple of things can come up.
Softer stools are the most common. That's a normal part of the adjustment for many dogs, and it usually settles within a few days.
Some dogs slow down at the bowl or seem less interested for a meal or two. That's not rejection. A new texture and temperature can take a little getting used to.
If {% if customer.dog_name %}{{customer.dog_name}}{% else %}your dog{% endif %} is doing fine, you don't need to do anything differently. Stay with the schedule.
If something feels like more than a normal adjustment, reply and tell me what you're seeing. I can help.
{{persona}} Protocol Raw
Notes: Secondary diagnostic metric (open rate, reply rate as friction signal). High reply rates here indicate transition problems, not satisfaction. Names the two most common Day 3-5 symptoms (soft stools, meal hesitation) and normalises both. Does not name vomiting, refusal, or diarrhoea (rarer, more serious. Customers experiencing those will reply regardless). Data requirements: first_name, dog_name, persona.
T4: Day 7 Milestone (Day 7) Layout: Relational
Subject: How's {% if customer.dog_name %}{{customer.dog_name}}{% else %}your dog{% endif %} getting on? Preview text: What to expect from here.
Hi {{first_name}},
{% if customer.dog_name %}{{customer.dog_name}}{% else %}Your dog{% endif %} should be fully on raw food now, or very close to it.
From here, things usually start to settle. You may notice stools getting firmer and smaller. Some dogs also drink a bit less water, which is normal on raw food.
You don't need to change anything. Keep feeding the amount on your schedule and let the routine do the work.
How's it going? I'd genuinely like to know. Just reply.
{{persona}} Protocol Raw
Notes: Core Golden Path metric (Steps 9-10: open rate, reply rate). Marks the transition milestone, then invites a reply. No professional self-identification prompt (kept clean for measurement purity; professional self-ID handled via reply-handling per Growth Playbook v2.4). Data requirements: first_name, dog_name, persona.
3.3b Pre-Box-2 Confidence (T6)¶
Customer.io Campaign: Pre-Box-2 Confidence
Trigger event: pre_box2_confidence (lifecycle event with scheduled_for = next_billing_date - 3 days)
Category: Lifecycle
From: Protocol Raw / hello@protocolraw.co.uk
Layout: Utility
Status: Built, inactive (pending launch activation)
Campaign workflow:
Subject: {% if customer.dog_name %}{{customer.dog_name}}'s{% else %}Your{% endif %} next box is coming soon Preview text: Everything is on track.
{% if customer.dog_name %}{{customer.dog_name}}'s{% else %}Your{% endif %} next box is coming up.
It will be the same recipe, tested before it ships, and delivered on schedule. If you need to skip, reschedule, or change your box size, you can do that from your portal before your billing date.
[CTA: Manage your plan -> {{portal_url}}]
Any questions, just reply.
Protocol Raw
Notes: Secondary diagnostic metric (open rate). Cadence-aware timing via scheduled_for on lifecycle event. Does not repeat billing date, ship date, price, or credit breakdown (the 7-day delivery reminder already covers those). One job: routine confidence reinforcement before second payment. The pre_box2_confidence lifecycle event is created by create_first_delivery_event and rescheduled by fn_reschedule_pre_box2_event_v1 on subscription changes. Edge case: if next_billing_date - 3 days has already passed at first delivery time, the event is not created and the 7-day reminder serves as the pre-Box-2 touch. Data requirements: dog_name, portal_url.
3.4 Founder Check-in (Day 10)¶
Trigger: first_box_delivered event
Timing: +10 days after first delivery
Category: Lifecycle (Phase A: manual from Gmail, Phase B: Customer.io campaign)
From: Anton at Protocol Raw anton@protocolraw.co.uk
Reply-to: anton@protocolraw.co.uk
Layout: None (Phase A manual) / Support Layout (Phase B Customer.io)
Subject: How's {% if customer.dog_name %}{{customer.dog_name}}{% else %}the first week{% endif %} going? Preview text: none
Hey {{customer.first_name}},
I'm Anton from Protocol Raw, and I wanted to check in now that {{customer.dog_name}}'s been on the food for about a week.
Every dog adjusts differently. Some take to it straight away, others need a little longer. Both are completely normal.
If anything feels unusual, or if you'd just like to tell me how it's going, just hit reply. I read every message myself.
Thanks, Anton Founder, Protocol Raw
Phase A execution: Manual send from Gmail. Supabase/Metabase query identifies customers whose first delivery was 10 days ago and who haven't cancelled. Log sends in spreadsheet.
Phase B switchover: When manual volume becomes impractical (~50+ customers), activate the Customer.io campaign and deactivate manual process.
3.5 Week 2 Check-in¶
Removed in v1.4. Replaced by the Transition Protection sequence (T1-T4, Section 3.3a) and Pre-Box-2 Confidence (T6, Section 3.3b) in v1.5. The founder email at Day 10 (T5) remains as the personal check-in touch.
4. Ongoing Subscription¶
4.1 7-Day Delivery Reminder¶
Trigger: pg_cron send-delivery-emails (daily 09:00 UTC)
Source function: get_pending_delivery_emails() checks subscriptions.next_billing_date - 7 days
Category: Operational
Status: Live (native Supabase, not Make.com)
Subject: {% if customer.dog_name %}{{customer.dog_name}}'s{% else %}Your{% endif %} next box ships in a week Preview text: Make any changes by {{cutoff_date}}.
Hi {{customer.first_name}},
{% if customer.dog_name %}{{customer.dog_name}}'s{% else %}Your{% endif %} next box ships in about a week.
Next delivery {{box_size}} box Ships: {{ship_date}} Charged: {{billing_date}} {% if has_credit %} {{list_price}} Credit: -{{credit_applied}} You'll pay: {{amount_due}} {% else %} {{list_price}}
Need to skip, reschedule, or change your box size? Make changes by {{cutoff_date}}: {{portal_link}}
Any questions, just reply.
Protocol Raw
Payload fields:
| Field | Type | Example | New in v1.4? |
|---|---|---|---|
| first_name | string | "Sarah" | No |
| box_size | string | "12kg" | No |
| price | string | "89" | No |
| billing_date | string | ISO date | No |
| ship_date | string | ISO date | No |
| cutoff_date | string | ISO date | No |
| portal_link | string | URL with token | No |
| has_credit | boolean | true | Yes |
| list_price | string | "£89" | Yes |
| credit_applied | string/null | "£15.00" | Yes |
| amount_due | string | "£74.00" | Yes |
4.2 48-Hour Lock¶
Trigger: pg_cron send-delivery-emails (daily 09:00 UTC)
Source function: get_pending_delivery_emails() checks subscriptions.next_billing_date - 2 days
Category: Operational
Status: Live (native Supabase, not Make.com)
Subject: {% if customer.dog_name %}{{customer.dog_name}}'s{% else %}Your{% endif %} box ships tomorrow Preview text: Changes are now locked.
Hi {{customer.first_name}},
{% if customer.dog_name %}{{customer.dog_name}}'s{% else %}Your{% endif %} next box ships tomorrow. Changes are now locked for this delivery.
This delivery {{box_size}} box Ships: {{ship_date}} {% if has_credit %} {{list_price}} Credit: -{{credit_applied}} You'll pay: {{amount_due}} {% else %} {{list_price}}
If you need to make urgent changes, reply to this email.
Protocol Raw
Note: The 48-hour event does NOT include cutoff_date (the cutoff has already passed). All other payload fields are the same as the 7-day reminder (see Section 4.1), including the four credit fields (has_credit, list_price, credit_applied, amount_due).
5. Subscription State Changes¶
5.1 Skip Confirmation¶
Customer.io ID: 9
Trigger: AFTER INSERT on raw_ops.subscription_actions (action_type = 'skip')
Timing: Immediate
Category: Transactional
Subject: Delivery skipped Preview text: Your plan is saved and ready whenever you are.
Hi {{customer.first_name}},
You've skipped your next delivery. No problem. Your plan remains active.
Next delivery {{subscription.box_size}} box, £{{subscription.price}} Ships: {{subscription.new_ship_date}} Charged: {{subscription.new_billing_date}}
You can adjust this anytime from your portal.
Manage your subscription
Any questions, just reply.
Protocol Raw
CTA: Manage your subscription -> {{portal_url}}
5.2 Reschedule Confirmation¶
Customer.io ID: 10
Trigger: AFTER INSERT on raw_ops.subscription_actions (action_type = 'reschedule')
Timing: Immediate
Category: Transactional
Subject: Delivery rescheduled Preview text: Your new delivery date is confirmed.
Hi {{customer.first_name}},
Your new delivery date is confirmed.
New delivery date {{subscription.box_size}} box, £{{subscription.price}} Ships: {{subscription.new_ship_date}} Charged: {{subscription.new_billing_date}}
Everything else on your plan remains the same. You can adjust your delivery anytime from your portal.
Manage your subscription
Any questions, just reply.
Protocol Raw
CTA: Manage your subscription -> {{portal_url}}
5.3 Pause Confirmation¶
Customer.io ID: 11
Trigger: AFTER INSERT on raw_ops.subscription_actions (action_type = 'pause')
Timing: Immediate
Category: Transactional
Subject: Subscription paused Preview text: Your plan is saved and ready whenever you are.
Hi {{customer.first_name}},
Your subscription is now paused. You won't be charged until you resume.
Subscription status {{subscription.box_size}} box, £{{subscription.price}} Status: Paused
Your plan remains saved and ready whenever you are.
When you're ready, you can resume from your portal and we'll schedule your next delivery.
Manage your subscription
Any questions, just reply.
Protocol Raw
CTA: Manage your subscription -> {{portal_url}}
5.4 Resume Confirmation¶
Customer.io ID: 12
Trigger: AFTER INSERT on raw_ops.subscription_actions (action_type = 'resume')
Timing: Immediate
Category: Transactional
Subject: Welcome back Preview text: Your subscription is active. Next delivery scheduled.
Hi {{customer.first_name}},
Welcome back. Your subscription is now active.
Next delivery {{subscription.box_size}} box, £{{subscription.price}} Ships: {{subscription.new_ship_date}} Charged: {{subscription.new_billing_date}}
You can adjust your delivery anytime from your portal.
Manage your subscription
Any questions, just reply.
Protocol Raw
CTA: Manage your subscription -> {{portal_url}}
5.5 Cancellation Confirmation¶
Customer.io ID: 13
Trigger: AFTER INSERT on raw_ops.subscription_actions (action_type = 'cancel')
Timing: Immediate
Category: Transactional
Subject: Subscription cancelled Preview text: You won't be charged again.
Hi {{customer.first_name}},
Your subscription has been cancelled. You won't be charged again.
If you decide to return in the future, we can help you pick up where you left off at protocolraw.co.uk.
Any questions, just reply.
Protocol Raw
Notes: No CTA button. Door left open with continuity language.
Also triggers: create_cancellation_event inserts subscription_cancelled lifecycle event (fires win-back campaign) and applies £10 win-back credit via create_customer_credit (source_type: cancellation_winback, 10-year expiry, one per customer, idempotent).
5.6 Box Size Change¶
Customer.io ID: 14
Trigger: AFTER INSERT on raw_ops.subscription_actions (action_type = 'box_change')
Timing: Immediate
Category: Transactional
Subject: Box size updated Preview text: Now {{subscription.box_size}} box, £{{subscription.price}}.
Hi {{customer.first_name}},
Your box size has been updated.
Your plan {{subscription.box_size}} box, £{{subscription.price}} Next delivery: {{subscription.new_ship_date}}
This applies from your next delivery. You can adjust anytime from your portal.
Manage your subscription
Any questions, just reply.
Protocol Raw
CTA: Manage your subscription -> {{portal_url}}
5.7 Frequency Change¶
Customer.io ID: 15
Trigger: AFTER INSERT on raw_ops.subscription_actions (action_type = 'frequency_change')
Timing: Immediate
Category: Transactional
Subject: Delivery schedule updated Preview text: Now every {{subscription.frequency_weeks}} weeks.
Hi {{customer.first_name}},
Your delivery schedule has been updated.
Your plan {{subscription.box_size}} box Every {{subscription.frequency_weeks}} weeks Next delivery: {{subscription.new_ship_date}}
This applies from your next delivery. You can adjust anytime from your portal.
Manage your subscription
Any questions, just reply.
Protocol Raw
CTA: Manage your subscription -> {{portal_url}}
5.8 Address Change¶
Customer.io ID: 16
Trigger: AFTER INSERT on raw_ops.subscription_actions (action_type = 'address_change')
Timing: Immediate
Category: Transactional
Subject: Delivery address updated Preview text: All future deliveries will go to your new address.
Hi {{customer.first_name}},
Your delivery address has been updated.
New delivery address {{customer.address1}} {{customer.city}} {{customer.zip}}
All future deliveries will go to this address.
If this wasn't you, please reply straight away.
Manage your subscription
Protocol Raw
CTA: Manage your subscription -> {{portal_url}}
Notes: Security line replaces "Any questions, just reply." Address formatted UK-style (postcode on own line, no comma).
6. Win-Back Sequence¶
6.1 Win-back 1 (30 Days)¶
Customer.io Campaign: Win-Back Sequence
Trigger event: subscription_cancelled (via lifecycle events pipeline)
Timing: +30 days after cancellation
Category: Lifecycle
From: Protocol Raw / hello@protocolraw.co.uk
Subject: You've got £10 credit on your account Preview text: If you'd like to restart, we've made it simple.
Ready when you are
Hi {{first_name}},
If you'd like to restart, there's £10 credit waiting on your account.
Protocol Raw is still the same simple setup: one complete recipe, the exact amount {% if dog_name %}{{dog_name}}{% else %}your dog{% endif %} needs, delivered on schedule.
[CTA: Restart your plan -> https://protocolraw.co.uk/pages/calculator]
If anything put you off last time, reply and tell us. We'd rather fix the problem than guess.
Protocol Raw
Notes: Lifecycle structure (headline, CTA, brand sign-off). No persona. No promo code. The £10 credit is auto-applied at cancellation via create_cancellation_event (source_type: cancellation_winback). One credit per customer, idempotent. Exit condition: subscription_status = active exits the campaign (customer resubscribed).
6.2 Win-back 2 (60 Days)¶
Customer.io Campaign: Win-Back Sequence (same campaign as Win-back 1)
Trigger event: subscription_cancelled
Timing: +60 days after cancellation
Category: Lifecycle
From: Protocol Raw / hello@protocolraw.co.uk
Subject: Your £10 credit is still there Preview text: It doesn't expire.
Your credit is still here
Hi {{first_name}},
This is the last time we'll reach out about your subscription. The £10 credit on your account doesn't expire.
[CTA: Restart your plan -> https://protocolraw.co.uk/pages/calculator]
If now wasn't the right time, that's okay. We'll be here if things change.
Protocol Raw
Notes: After this email, silence. No win-back 3. No escalation. Same offer, no urgency. The credit genuinely does not expire (10-year TTL). Exit condition: subscription_status = active exits the campaign.
7. Calculator Abandonment Sequence¶
7.1 Abandonment 1h¶
Trigger: pg_cron send-abandonment-events-hourly (hourly)
Timing: +1-2 hours after calculator completion, no order
Category: Lifecycle
Status: Live
Subject: {{if dog_name}}{{dog_name}}'s{{else}}Your{{end}} feeding plan Preview text: {{daily_grams}}g per day. Ready when you are.
Hi,
You calculated a feeding plan but didn't place an order. The details are saved.
Your plan {{box_size}} box, £{{discounted_price}} (£20 off) {{daily_grams}}g per day for {{if dog_name}}{{dog_name}}{{else}}your dog{{end}} Delivered every {{delivery_weeks}} weeks
Use code {{discount_code}} at checkout.
Continue your order
Protocol Raw
CTA: Continue your order -> {{product_url}}?token={{token}}
7.2 Abandonment 48h¶
Trigger: pg_cron send-abandonment-events-hourly (hourly)
Timing: +48-50 hours after calculator completion, no order, 1h email sent
Category: Lifecycle
Status: Live
Subject: Still thinking it over? Preview text: Your plan is still saved.
Hi,
Your feeding plan for {{if dog_name}}{{dog_name}}{{else}}your dog{{end}} is still saved. A few things that might help:
Safety Every batch is tested by an independent UKAS-accredited lab before release. When your box arrives, scan the QR code on any pouch to see the results for your specific batch.
Transition We include a 10-day schedule with exact gram amounts. Most dogs adjust within the first week or two.
Your code {{discount_code}} is still valid for £20 off.
Continue your order
Protocol Raw
CTA: Continue your order -> {{product_url}}?token={{token}}
8. Technical Implementation¶
8.1 Architecture Overview¶
Protocol Raw uses three email dispatch patterns:
Pattern A: Immediate transactional emails Triggered by database events, sent via Customer.io Transactional API.
Portal/System action
|
v
Database INSERT (orders / subscription_actions / shipments)
|
v
AFTER INSERT trigger function (vault-backed credentials, audit logged)
|
v
pg_net async HTTP call to send-lifecycle-email Edge Function
|
v
Customer.io Transactional API
Pattern B: Scheduled operational emails Queued via pg_cron, sent via Customer.io Track API or outbox.
pg_cron job (e.g. send-delivery-emails, send-abandonment-events)
|
v
fn_invoke_edge_function wrapper (vault-backed, no hardcoded credentials)
|
v
Edge Function identifies eligible customers, sends Track events or writes to outbox
|
v
Customer.io Track API / email_outbox -> process-outbox
Pattern C: Campaign lifecycle emails (new in v1.3)
Events queued in lifecycle_events table, processed by pg_cron, trigger Customer.io campaigns.
Database trigger (order/shipment/subscription action)
|
v
Event creator function (create_order_created_event / create_first_delivery_event / create_cancellation_event)
|
v
create_lifecycle_event (idempotent via idempotency_key, enriched with persona/name/dog)
|
v
raw_ops.lifecycle_events table (state: pending)
|
v
process-lifecycle-events pg_cron (every minute) via fn_invoke_edge_function
|
v
send-lifecycle-events Edge Function
|
v
1. Customer.io Identify call (syncs profile attributes: first_name, dog_name, persona, subscription_status, etc.)
2. Customer.io Track event (fires campaign trigger)
|
v
Customer.io Campaign workflow (delays, branches, sends)
8.2 Customer.io Configuration¶
Credentials are stored in Supabase Edge Function secrets and Vault. Never hardcode in documentation or code.
| Secret | Edge Function Env Var | Vault Name | Usage |
|---|---|---|---|
| Site ID | CUSTOMERIO_SITE_ID |
-- | Track API Basic auth |
| Track API Key | CUSTOMERIO_API_KEY |
-- | Track API Basic auth |
| App API Key | CUSTOMERIO_APP_API_KEY |
-- | Transactional API Bearer token |
| Service Role Key | -- | service_role_key |
Database trigger pg_net calls |
| Anon Key | -- | anon_key |
pg_cron Edge Function calls |
8.3 Customer.io Message IDs¶
| Customer.io ID | Template ID | |
|---|---|---|
| Order Confirmation (First) | 6 | order_confirmation_first |
| Order Confirmation (Repeat) | 7 | order_confirmation_repeat |
| Portal Access Magic Link | 3 | portal_access |
| Skip Confirmation | 9 | delivery_skipped |
| Reschedule Confirmation | 10 | delivery_rescheduled |
| Pause Confirmation | 11 | subscription_paused |
| Resume Confirmation | 12 | subscription_resumed |
| Cancellation Confirmation | 13 | subscription_cancelled |
| Box Size Change | 14 | box_size_changed |
| Frequency Change | 15 | frequency_changed |
| Address Change | 16 | address_updated |
| Dispatch Confirmation | 17 | dispatch_confirmation |
| Delivery Confirmation | 18 | delivery_confirmation |
8.4 Customer.io Campaigns¶
| Campaign | Trigger Event | Workflow | Status |
|---|---|---|---|
| Transition Guide | order_created_first |
Wait 1 day -> Send transition guide | Built, inactive |
| Transition Protection | first_box_delivered |
Wait 2h -> T1 -> Wait 2d -> Branch (active?) -> T2 -> Wait 2d -> Branch (active?) -> T3 -> Wait 3d -> Branch (active?) -> T4 | Built, inactive |
| Pre-Box-2 Confidence | pre_box2_confidence |
Branch (active?) -> T6 | Built, inactive |
| [RETIRED] Onboarding Check-ins | first_box_delivered |
(Retired. Replaced by Transition Protection.) | Retired, inactive |
| Win-Back Sequence | subscription_cancelled |
Wait 30d -> Win-back 1 -> Wait 30d -> Branch (not active?) -> Win-back 2 | Built, inactive |
Exit conditions: Transition Protection, Pre-Box-2 Confidence, and Win-Back campaigns branch on subscription_status attribute (synced via identify call in send-lifecycle-events).
8.5 Edge Functions¶
| Function | Purpose | Trigger | Pattern |
|---|---|---|---|
send-lifecycle-email |
Send single transactional email | Database trigger via pg_net | A |
send-lifecycle-events |
Process lifecycle event queue + identify | pg_cron (every minute) | C |
process-outbox |
Process email outbox queue | pg_cron (every minute + every 2 min) | B |
send-delivery-emails |
Send 7-day/48-hour reminders. Now includes credit data (has_credit, list_price, credit_applied, amount_due) via LEFT JOIN to customer_credits in get_pending_delivery_emails(). | pg_cron (daily 09:00 UTC) | B |
send-abandonment-events |
Send calculator abandonment emails | pg_cron (hourly) | B |
request-portal-access |
Send magic link on demand | HTTP (portal login page) | A |
send-portal-access-emails |
Proactive portal access for new customers | pg_cron (daily 10:00 UTC) | B |
8.6 Lifecycle Event Creator Functions¶
| Function | Trigger | Event Name | Additional Actions |
|---|---|---|---|
create_order_created_event |
trigger_order_lifecycle_event on orders |
order_created_first / order_created_repeat |
Enriches with dog_name, box_size, portal_url, transition_url |
create_first_delivery_event |
trigger_shipment_email on DELIVERED |
first_box_delivered |
Only fires for first delivery (COUNT = 1). Also creates pre_box2_confidence lifecycle event with scheduled_for = next_billing_date - 3 days (v1.5). |
fn_reschedule_pre_box2_event_v1 |
Called from trigger_subscription_action_email on skip/reschedule/frequency_change/box_change/pause/resume/cancel |
pre_box2_confidence |
Updates scheduled_for on pending event, or deletes on pause/cancel. |
create_cancellation_event |
trigger_subscription_action_email on cancel |
subscription_cancelled |
Creates £10 win-back credit (idempotent, one per customer) |
8.7 Customer.io Profile Attribute Syncing¶
send-lifecycle-events performs a Customer.io Identify call before every Track event. This syncs profile attributes from the lifecycle event data:
| Attribute | Source | Set by events |
|---|---|---|
first_name |
customers table | All events |
dog_name |
calculator_discounts/dogs | All events |
persona |
Deterministic hash | All events |
portal_url |
Static | All events |
transition_url |
Static deep link | order_created_first, first_box_delivered |
box_size |
subscription/order | All events |
subscription_status |
Derived from event name | order_created -> active, paused -> paused, cancelled -> cancelled, resumed -> active |
timezone |
Default Europe/London | All events |
8.8 Database Triggers¶
All triggers use AFTER INSERT (not BEFORE INSERT). All use vault-backed credentials via vault.decrypted_secrets. All log to raw_ops.ops_events.
| Trigger | Table | Events | Functions Called |
|---|---|---|---|
trg_order_confirmation |
raw_ops.orders |
AFTER INSERT | trigger_order_confirmation -> send-lifecycle-email |
trg_order_lifecycle_event |
raw_ops.orders |
AFTER INSERT/UPDATE | trigger_order_lifecycle_event -> create_order_created_event -> send-lifecycle-events |
trg_subscription_action_email |
raw_ops.subscription_actions |
AFTER INSERT | trigger_subscription_action_email -> send-lifecycle-email + create_cancellation_event (on cancel) |
trg_shipment_email |
raw_ops.shipments |
AFTER INSERT/UPDATE | trigger_shipment_email -> send-lifecycle-email + create_first_delivery_event (on first DELIVERED) |
8.9 pg_cron Jobs¶
All Edge Function calls route through fn_invoke_edge_function (vault-backed, no hardcoded credentials).
| Job | Schedule | Function/Edge Function | Purpose |
|---|---|---|---|
process-lifecycle-events |
Every minute | send-lifecycle-events |
Process pending lifecycle events |
retry-lifecycle-events |
Every 5 min | retry_failed_lifecycle_events() |
Retry failed events |
process-outbox-every-minute |
Every minute | process-outbox |
Process email outbox |
process-outbox-every-2-min |
Every 2 min | process-outbox |
Secondary outbox processing |
send-delivery-emails |
Daily 09:00 UTC | send-delivery-emails |
7-day and 48-hour reminders |
send-abandonment-events-hourly |
Hourly | send-abandonment-events |
Calculator abandonment |
send-portal-access-emails |
Daily 10:00 UTC | send-portal-access-emails |
Proactive portal access |
fallback-first-delivery-events |
Daily 07:00 UTC | fn_fallback_first_delivery_events_v1() |
Safety net for missed DPD delivery scans |
canary-system-health |
Every 10 min | fn_canary_system_health_v1() |
Independent system health check |
8.10 Portal Integration¶
| Portal | Status | Flow |
|---|---|---|
| Ops Portal | Wired | Calls log_subscription_action -> AFTER INSERT trigger -> transactional email + lifecycle event |
| Customer Portal | Wired (v1.3) | Calls log_subscription_action via Supabase RPC (non-blocking) before Make.com webhook |
8.11 Email Outbox (Pattern B)¶
Table: raw_ops.email_outbox
State machine:
| State | Meaning | Next |
|---|---|---|
pending |
Queued, not yet sent | dispatched on success, stays pending on failure |
dispatched |
Customer.io accepted | Terminal success |
failed |
Max attempts (5) exhausted | Terminal failure, critical alert |
Exponential backoff: Immediate, 5 min, 15 min, 1 hour, 6 hours.
8.12 Lifecycle Events Queue (Pattern C)¶
Table: raw_ops.lifecycle_events
Key features: idempotency via idempotency_key (unique), status tracking (pending/processing/sent/failed), retry with attempts/max_attempts, scheduled_for for delayed sends, error history, Customer.io delivery tracking.
Writer: create_lifecycle_event() enriches with customer persona/name, generates idempotency key, inserts with ON CONFLICT DO NOTHING.
8.13 Monitoring and Observability¶
Standard monitors (via SOP-MON-01 pattern):
| Monitor | Schedule | Channel | Thresholds |
|---|---|---|---|
monitor-outbox-health |
Every 2 min | #ops-alerts / #ops-urgent | Warning: stuck > 10 or pending > 50. Critical: stuck > 50 or pending > 200 |
monitor-lifecycle-events |
Every 15 min | #ops-alerts | Lifecycle event processing lag |
Independent canary monitor (does NOT use fn_invoke_edge_function):
| Monitor | Schedule | Channel | Purpose |
|---|---|---|---|
canary-system-health |
Every 10 min | #ops-urgent | Detects if fn_invoke_edge_function or critical pg_cron jobs are down. Own vault lookup, own pg_net call, completely independent alert path. |
Canary checks: process-outbox-every-minute, process-lifecycle-events, monitor-outbox-health. If any haven't succeeded in 15 minutes, fires alert to #ops-urgent via independent path. Logs to raw_ops.canary_alerts table.
Business performance metrics:
| Metric | Target | Alert Threshold |
|---|---|---|
| Box-2 retention | >=70% | <65% |
| Unsubscribe rate | <0.5% | >1% |
| Spam complaint rate | <0.1% | >0.2% |
| Reply rate (check-ins) | >5% | <2% |
Email performance targets:
| Email Type | Open Rate | Click Rate |
|---|---|---|
| Transactional | 70%+ | N/A |
| Operational | 60%+ | 20%+ |
| Lifecycle | 50%+ | 15%+ |
| Win-back | 30%+ | 10%+ |
| Abandonment | 40%+ | 15%+ |
8.14 Error Handling and Resilience¶
Pattern A (immediate): pg_net is async and non-blocking. Edge Function failures don't block database commits. Manual resend: call send-lifecycle-email directly. No automatic retry (acceptable at Phase A volumes).
Pattern B (outbox): Retry up to 5 times with exponential backoff. After 5 failures: state -> failed, critical alert to #ops-urgent. Manual replay: UPDATE raw_ops.email_outbox SET state = 'pending', attempts = 0 WHERE id = '<uuid>';
Pattern C (lifecycle events): Retry via retry-lifecycle-events pg_cron job (every 5 min). Idempotency via unique idempotency_key prevents duplicate sends. send-lifecycle-events identify errors are logged but don't fail the track event.
System-level resilience: Independent canary monitor (fn_canary_system_health_v1) detects infrastructure failures within 15 minutes via a completely separate alert path. This prevents silent outages like the 26-hour fn_invoke_edge_function failure.
9. Design Standards¶
All emails follow the Protocol Raw Email Design System v1.3.
9.1 Quick Reference¶
| Element | Standard |
|---|---|
| Background | Cream #F9F7F4 (applies to card area too) |
| Body text | Espresso #2B2523, 16px Inter |
| Headlines | Espresso #2B2523, 24px Montserrat Bold |
| Links | Burnt Sienna #B85C3A |
| Primary CTA | Burnt Sienna #B85C3A background, white text |
| Info cards | Warm Linen #EBE8E3 background |
| Max width | 600px |
| Header | 4px accent line (Burnt Sienna or Forest Green) + "Protocol Raw" wordmark below |
| Font stacks | Montserrat, Trebuchet MS, Arial, sans-serif (headlines). Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Arial, sans-serif (body) |
| Hidden preheader | Required on all emails. Matches preview text field in Customer.io |
9.2 Sign-off Rules¶
| Email Type | Sign-off | Layout |
|---|---|---|
| Transactional | Protocol Raw | Utility |
| Operational | Protocol Raw | Utility |
| Proof/Delivery | Protocol Raw | Proof |
| Lifecycle (standard) | Protocol Raw | Utility |
| Lifecycle (relational) | {{persona}}, Protocol Raw | Relational |
| Lifecycle (founder, Phase A) | Anton, Founder, Protocol Raw | None / Support |
| Support | (body contains sign-off) | Support |
9.3 Warmth Pattern¶
All transactional emails end with "Any questions, just reply." except: - Cancellation (door-open language instead) - Address Change (security line instead)
10. What NOT to Email¶
Silence is sometimes the stronger move. We do NOT send:
- Week 3 or Week 4 check-ins (founder email at day 10 is the last onboarding touch)
- Win-back 3+ (two touches maximum, then silence)
- "We miss you" or guilt-based messaging
- Promotional emails or sales
- Newsletter/content marketing (not a newsletter brand)
- Survey requests (unless specific research need)
- NPS scores (doesn't fit brand)
- Birthday emails (don't collect DOB)
- Anniversary emails (feels performative)
- Promo codes in win-back emails (use auto-applied account credit instead)
11. Version History¶
| Version | Date | Changes |
|---|---|---|
| 1.0 | January 2026 | Initial release. Consolidated all lifecycle email copy. |
| 1.1 | February 2026 | Implemented transactional emails in Customer.io. Added database trigger architecture. Updated copy with warmth pass. Added Box Size, Frequency, Address change emails. Documented Customer.io IDs and Edge Function. |
| 1.2 | March 2026 | Security: redacted hardcoded Customer.io credentials. Architecture: rewrote Section 8 to document actual production system (outbox pattern, lifecycle events pipeline, monitoring). Fixed BEFORE INSERT trigger to AFTER INSERT. Added monitoring section and error handling section. Corrected document status. Fixed UTF-8 encoding. |
| 1.3 | March 2026 | All lifecycle campaigns built in Customer.io (Transition Guide, Onboarding Check-ins, Win-Back Sequence). Revised copy: transition guide single-purpose, check-ins delivery-anchored with affirming language, win-back redesigned as lifecycle structure with auto-applied £10 credit (no promo codes). Added Pattern C (lifecycle events pipeline with identify + track). Documented all new infrastructure: create_first_delivery_event, create_cancellation_event, fn_invoke_edge_function wrapper, canary monitor, delivery fallback job. Customer Portal wired to log_subscription_action. Confirmed 7-day/48-hour and dispatch/delivery emails as live. End-to-end pipeline verified. |
| 1.5 | April 2026 | Transition Protection sequence added (T1-T4, T6), replacing Week 1/Week 2 onboarding check-ins. T1 Arrival Guide (Day 0, Utility), T2 Day 2 Check-in (Day 2, Relational), T3 Day 4 Normalisation (Day 4, Relational), T4 Day 7 Milestone (Day 7, Relational), T6 Pre-Box-2 Confidence (cadence-aware, Utility). Founder check-in (T5, Day 10) unchanged. Old Onboarding Check-ins campaign retired. New campaigns: Transition Protection (trigger: first_box_delivered) and Pre-Box-2 Confidence (trigger: pre_box2_confidence). Pre-Box-2 event created by extended create_first_delivery_event, rescheduled by new fn_reschedule_pre_box2_event_v1. Golden Path metrics remapped in Growth Playbook v2.4. |
| 1.4 | April 2026 | Email Design System v1.3 migration. All templates moved to Layouts (Utility, Proof, Relational, Support). Founder email replaces Week 1/Week 2 check-ins for Phase A (day 10, manual from Gmail). Week 2 check-in dropped. Credit breakdown added to 7-day and 48-hour delivery reminders. Win-back campaign built (2-email sequence, 30-day spacing). Orphaned campaigns deleted (delivery_reminder_7day, delivery_lock_notification). All CTA buttons replaced with inline portal links except payment, dispatch, tracking, and proof emails. |
12. Related Documentation¶
- Email Design System v1.3: Visual and copy standards
- SOP-EMAIL-01: Email architecture and sender configuration
- SOP-CS-00: Customer Operations System (email matrix, triggers)
- SOP-REF-01: Referral System (referral/credit emails, credit system)
- SOP-SUB-00: Subscription State Management (dunning/card expiry emails)
- SOP-DLV-01: Courier Watchdog (delivery exception emails)
- SOP-MON-01: Monitoring and Alerting Architecture (outbox health, lifecycle events monitoring)
- Technical Operations Handbook v1.1: Complete pg_cron job registry and Edge Function inventory
- Abandonment Recovery System: Technical implementation for calculator abandonment
13. Launch Activation Checklist¶
Before activating campaigns at launch:
- [ ] Activate Transition Guide campaign in Customer.io
- [ ] Activate Onboarding Check-ins campaign in Customer.io
- [ ] Activate Win-Back Sequence campaign in Customer.io
- [ ] Verify
subscription_statusis flowing to Customer.io profiles (test with a subscription action) - [ ] Verify
personais set on test customer profiles (not falling back to default) - [ ] Confirm
canary-system-healthpg_cron job is active and has recent successful runs - [ ] Confirm
fallback-first-delivery-eventspg_cron job is active - [ ] Send test emails through each campaign to verify rendering in Gmail, Apple Mail, Outlook
- [ ] Activate Transition Protection campaign in Customer.io
- [ ] Activate Pre-Box-2 Confidence campaign in Customer.io
- [ ] Verify fn_reschedule_pre_box2_event_v1 is deployed and tested
- [ ] Verify pre_box2_confidence event is created on test first delivery
- [ ] Verify pre_box2_confidence event is rescheduled on test subscription change
- [ ] Send test emails through Transition Protection campaign (T1-T4) to verify rendering
- [ ] Send test email through Pre-Box-2 Confidence campaign (T6) to verify rendering
End of SOP-LC-01 v1.5