Maczfit Customer.io data architecture | Meal-kit ecommerce
I designed an event-based Customer.io model for Maczfit so high-volume carts and orders stay off Objects, with Objects reserved for durable entities such as subscriptions and diet plans. Maczfit's engineering team implemented the architecture. Result: more than €50,000 saved per month.
For ecommerce teams whose billable-profile ceiling collapses when carts and orders are modelled as Objects.
Monthly savings after Maczfit engineering implemented the event-based architecture I designed.
Carts, orders, and packages carried as events and attributes, not Objects.
Reserved for durable entities such as an active subscription or a diet plan.
What the engagement covered.
I designed the event-based Customer.io model. Maczfit's engineering team implemented it.
Maczfit is a Polish meal-kit and diet ecommerce business running Customer.io at scale. I designed the data model, the split between the warehouse preparation layer and Customer.io, the first journey shapes (abandoned cart and unpaid order), and the implementation path.
I own the design, not the warehouse build or production flow configuration.
The ecommerce platform stayed the source of truth for commerce state. A cloud data warehouse was the designed v1 preparation layer: snapshot comparison, a status-change log, and an event outbox before events reached Customer.io. Customer.io stayed the activation layer for email, push, and SMS.
What was broken at the start.
Billable profiles count people and Objects together. High-volume carts as Objects would burn that ceiling in days.
Modelling carts, orders, or packages as Objects would either block sending or drive large overage cost. Treating Customer.io as a warehouse of ecommerce history made the same problem worse: the platform was being asked to hold more state than an activation layer should.
Warehouse refresh on a minutes-scale cadence meant intermediate statuses inside one window might never emit as separate events. That constraint shaped wait logic and exit conditions in the journeys I designed.
In v1, events for these scenarios come from snapshot comparison, not real-time emission from the ecommerce platform.
How the data model was rebuilt.
Events for history. Attributes for current state. Objects only for durable, low-volume entities.
Events carry history, triggers, and status changes for carts, orders, packages, checkout, payment, and delivery. Attributes hold current customer state and exit conditions. Objects stay limited to entities such as an active subscription or a diet plan.
Carts, orders, and packages stay off Objects so billable-profile growth tracks people, not daily commerce volume.
The model used a small set of commerce lifecycle events, a stable idempotency key for deduplication and outbox retry, and profile attributes for exit checks.
Categories covered (names withheld): an observed-cart event for abandoned cart; an observed unpaid-order event; cart-state attributes on the profile; order and payment state for exits; light personalisation attributes. Customer.io receives business-relevant events, not a copy of the ecommerce history.
How the rebuild ran, step-by-step.
Not a migration from another ESP. A two-phase path for Maczfit engineering, blocked on warehouse payload fields.
Phase 1 locked payload fields, naming, and the outbox. Phase 2 wired Customer.io, attached attributes, and tested edge cases before warmup. The blocking dependency was payload field availability in the warehouse.
- Phase 1DesignModel + path
Lock the architecture
I designed the event-based model, the warehouse-versus-Customer.io split, and the first journey shapes before any production wiring.
- +Events for high-volume history
- +Attributes for current exit state
- +Objects only for durable entities
- Phase 1WarehouseEngineering build
Prepare the outbox
Maczfit engineering confirmed payload fields, agreed event naming, and built snapshot comparison, a status-change log, and an event outbox.
- +Stable idempotency keys for retry
- +Pending, sent, and failed outbox states
- +Minutes-scale refresh cadence constraints
- Phase 2WireCustomer.io
Attach and test
Engineering sent test events into Customer.io, attached profile attributes, and built flows with wait and state checks before any full-base send.
- +Edge cases: fast status hops, missing ids, duplicates
- +Separate transactional and marketing subdomains
- +Domain warmup before full-base sends
- OngoingMonitorBillable profiles
Watch volume and exits
Specified monitoring covered event volume, duplicates, entries, exits, sends, conversions, and billable-profile usage after go-live.
- +Hard-bounce removal on sight
- +Exit checks before every reminder
- +Keep Customer.io as an activation layer
Before versus after, side by side.
Objects-for-carts versus an event-based activation model.
Dumping full ecommerce history into Customer.io looked complete in a design review and failed under billable-profile math within days of real cart and order volume in 2026.
| Objects for carts | Event-based model | Full warehouse in CIO | |
|---|---|---|---|
| Billable-profile growthTracks people, not daily commerce volume. | ✕ | ✓ | ✕ |
| Carts and orders as eventsHistory and triggers stay off Objects. | ✕ | ✓ | ~ |
| State check before reminderNever send from the observed event alone. | ~ | ✓ | ~ |
| Warehouse prep layer (v1)Snapshot comparison, status log, outbox. | ✕ | ✓ | ✕ |
| Objects for subscriptions onlyDurable, low-volume entities stay selective. | ✕ | ✓ | ~ |
What changed after launch.
After engineering implemented the architecture I designed, the engagement saved more than €50,000 per month.
The data model rule changed: events for high-volume history, attributes for current state, Objects only selectively. The warehouse prepares and deduplicates events in v1; Customer.io consumes ready events.
I designed abandoned-cart and unpaid-order journey shapes with a current-state check before every reminder. This page does not claim I configured the live flows, or that every item in the architecture fully shipped, only the design, the engineering handoff, and the savings figure above.
Related reading: the CRM implementation playbook and IP warmup planner.
Questions people ask.
Billable-profile ceilings, Objects limits, warehouse ownership, and state checks before reminders.
/01Did you implement the warehouse yourself?+
/02Why keep carts and orders off Objects?+
/03What journeys did you design first?+
/04How did deliverability split?+
/05What blocked Phase 1?+
/06Did ecommerce-direct emission (v2) ship?+
Need Customer.io data architecture like this?
Most engagements start with a short call to review workspace, volume, and billable-profile constraints.
Bring Objects usage and warehouse field availability.